sábado, 26 de abril de 2008

[Opinião] Criacionismo e intolerância religiosa

Este post é decorrente da inquietude em mim gerada por um vídeo enviado por um amigo, apresentado abaixo.



O vídeo, muito bem produzido por sinal, apresenta a ciência (em particular, a teoria da evolução das espécies de Darwin) como opressora e inflexível a opniões divergentes (em particular, ao criacionismo defendido por católicos evangélicos). Nele, a "era da ciência" e a "era das máquinas" são vistas como tiranos que deve ser combatidos pois acarretarão no fim das religiões e da liberdade. Neste escopo, o autor questiona o "fato" de que os PhDs devem ser ouvidos cegamente, e qualquer opnião diversa deve ser excluída! A grande questão aqui é que o vídeo (além do fato de estar em inglês) é bem feito e tem potencial para gerar desinformação.

Não é estranho de se imaginar que tal vídeo tenha relação com a opnião expressa por este outro criacionista, que defende o dogma da criação do homem ao referenciar o pensamento crítico por "síndrome de lúcifer", com citações bíblicas inclusive.

Neste contexto, sou um PhD em engenharia biomédica, logo um dos famigerados "opressores". Dentre as minhas atividades, nos últimos 3 semestres chequei a orientar a incrível soma de cerca de 100 monografias de trabalhos de conclusão de curso em nível superior (fora o trabalho, aulas e demais orientados). Algo que me marcou foi que realmente pude passar os fundamentos do método científico para muitas pessoas (algo que realmente só se constroi ao realizar um trabalho científico) e percebi claramente a modificação do seu pensamento: fixou-se a importância do questionamento e da fiabilidade da informação. Estas pessoas vão levar tais princípios para suas vidas!

Afinal, dentre outras coisas, um dos fundamentos da ciência é o método socrático! Tal príncipio discute a descontrução do pensamento mítico pela ironia, e a indução da razão pelo questionamento e diálogo como caminho para a verdade.

Neste contexto, Sócrates discute que deve-se, de forma não ofensiva, mostrar ao interlocutor o absurdo de suas idéias evidenciando as contradições e incoerências lógicas do discurso através do diálogo aberto (exatamente o oposto sugerido pelo vídeo, que mostra a ciência como intolerante). Desta forma, a ironia (i.e. evidenciamento das contradições) é um instrumento para a construção de idéias livres de presupostos superficiais ou preconceitos.

Na discursão sobre criacionismo e a teoria da evolução das espécies, argumenta-se:

1) Não existem evidências materiais da criação divina defendida pela teologia judaico-cristã (ou por qualquer outra tradição religiosa), apenas os escritos antigos.

2) A evolução e a abiogênese são apoiados em evidências:

  • Bioquímicas: o surgimento da vida a partir da matéria inanimada (abiogênese) expressa na Hipótese Oparin-Haldane ou Hipótese Heterotrófica é comprovada pelo experimento de Urey-Miller de 1953.
  • Arqueológicas: a evolução das espécies é evidenciada pela existência dos fosséis, objeto de estudo da palenteologia (lembra dos dinossauros em Jurasic Park?!). Quanto à origem das espécies e do homem em particular, todos os processos de avaliação da idade dos fósseis tanto animais como do próprio homem e de seus precursores mais imediatos apontam números totalmente incompatíveis com os fixados pelos textos religiosos.
  • Genéticas: a evolução das espécies é evidenciada pela existência da mutação do DNA e pela seleção natural de genes, comprovada pelos estudos de Ronald Fisher (que desenvolveu a linguagem matemática e a seleção natural nos termos essenciais dos processos genéticos), J. B. S. Haldane (que introduziu o conceito de "custo" da seleção natural), Sewall Wright (que elucidou a natureza da seleção e da adaptação), Theodosius Dobzhansky (que estabeleceu a idéia de que mutações, ao criarem diversidade genética, supriam o material bruto para a seleção natural), William Hamilton (que concebeu a seleção parentada), Ernst Mayr (que reconheceu a importância chave do isolamento reprodutivo para a especiação). Tais estudos são verificáveis nos contextos de genética de populações, biologia celular, botânica, zoologia, sociobiologia e ecologia.
  • Computacionais: a evolução das espécies é evidenciada pelas soluções evolutivas, em particular aos algoritmos genéticos. Pessoalmente, acho que nada mais visual neste sentido do que o projeto golem (excelente para leigos!).
  • Antropológicas e sociológicas: pelos mecanismos de seleção artificial em populações para aptidão econômica e social, demonstrando matemáticamente causas genéticas para comportamentos como seleção consangüínea (kin selection), altruísmo, especiação e os memes. Esta nova área do conhecimento vêm sendo referida por sociobiologia (portal). Um dos ramos interessantes desta linha de pesquisa é a psicologia evolutiva.
  • Biológicas: Sua aplicação pode ser observada em diversos processos, particularmente de forma mais evidente na cladística e na a biologia do desenvolvimento (i.e. estudo do desenvolvimento embrionário).
  • Matemáticas e econômicas: A seleção natural é aplicada nos mecanismos de equilíbrio econômico (teoria da escolha racional), baseados na teoria dos jogos. A teoria dos jogos, fundamentada nos matemáticos e economistas John von Neumann e Oskar Morgenstern e que também inclui as contribuições de John Harsanyi, John Nash, Howard Raiffa, Thomas C. Schelling e Herbert Simon. Esta formulação também tem aplicações em filosofia e ciência política, ética e jornalismo.
  • Cosmológicas e geológicas: Embora não relacionadas com a evolução, a cosmologia, astrofísica e geologia também atestam pela inveracidade científica do mito de criação do universo pela teologia judaico-cristã (na qual se insere a criação divina da vida e da própria espécie humana), assumindo seu início pelo Big Bang.
Desta forma, a negação da evolução envolve a recusa em aceitar uma boa parte das ciências naturais, principalmente as descrições da história do planeta e da vida.

Dentre as hipotéses (1) e (2) reina uma questão de bom senso (assim como assumido pela navalha de Ockham)!

A recente discussão do criacionismo por grupos evangélicos é decorrente da estratégia da cunha (ao qual se insere o vídeo motivador deste post), defendendo o criacionismo pelo design inteligente, que consiste de uma pseudo-ciência (classifica-se como pseudo-ciência por não apresentar evidências materiais ou seguir o método científico). Especula-se que este movimento faça parte de uma estratégia política de grupos conservadores norte-americanos (particularmente do sul dos Estados Unidos), obtendo favorecimento eleitoral ao subsidiar a agenda social conservadora em uma ampla gama de assuntos defendidos por grupos evangélicos incluindo aborto, eutanásia, sexualidade e outros movimentos de reforma social.

Neste ponto, não tanto como pesquisador (acho que o tal título de D.Sc. me concede tal denominação), mas como humanista e cidadão consciente (de visão liberal e social-democrática) me vejo na obrigação de desmistificar e desconstruir a desinformação causada pelos dogmas criacionistas e do design inteligente, ainda mais tendo em vista a implicação política e social implícita. No Brasil, o problema não é menos sério, conforme o post anterior.

Neste ponto, me insurjo em convocar as demais pessoas de bom senso, que acreditem na humanidade, a empregar esforços para conter a intolerância e fundamentalismo religioso sempre que o mesmos:
  • Ameaçem as liberdades democráticas: e.g. discriminação ou resarciamento da liberdade de crença contra praticantes de cambomblé por grupos evangélicos pentecostais;
  • Incitem a violência, o preconceito e a intolerância: e.g. conflito palestino, etc;
  • Impuserem a exploração social/econômica do indivíduo ou a perda de sua qualidade de vida em proveito evidente de terceiros: e.g. instituições que induzem doações ou cobram dízimos - principalmente de pessoas com um salário ou menos - sem apresentar prestações de contas abertas e públicas (demostrativos de usos e fontes) que mostrem claramente o uso do dinheiro - Igreja Universal, Assembléia de Deus, etc.
  • Travestirem informações como científicas de forma a obter credibilidade: e.g. espiritismo, design inteligente/criacionismo, cientologia, noergologia, homeopatia, astrologia, radioestesia, e demais pseudociências esotéricas. Note que este problema assume uma dimensão muito mais preocupante na medida em que tais "áreas do conhecimento" alcançam o ensino superior formal, com o apoio do estado inclusive.
  • Incidam na separação entre estado e igreja: e.g. pressão contra pesquisa de células tronco no Brasil.

No entanto, cabe discutir que deve-se haver um total respeito pela liberdade de crença e o livre arbítrio em qualquer situação, cabendo apenas a intervenção e esclarecimento sobre aspectos negativos do fundamentalismo religioso (principalmente nos contextos descritos acima). Desta forma, a conclusão sobre o tema a ser discutida com os criacionistas supracitados deve serguir um formato geral semelhante a:


"Você tem todo o direito de ter suas crenças e não pretendo contestá-las! Você pode acreditar em Deus e que o mesmo criou o homem se assim o quiser, mas deve concordar que isto claramente não é uma verdade científica! Sua aceitação destes dogmas baseia-se apenas em sua fé, e não na lógica e nos fatos. As evidências mostram que a evolução das espécies é real e a vida se originou pela abiogênese. É possível crer em Deus e entender a ciência, desde que não se tente misturá-los ou conflitá-los e que não se obrigem terceiros aos dogmas, especialmente terceiros de outras crenças. A crença em Deus é uma verdade pessoal (cada um tem o seu) e a ciência é uma verdade universal (a natureza é a mesma para todos)!"


Observe-se um ponto fundamental no discurso acima: a sutileza e a não ofensividade. Deve-se sempre evitar a negação simples e direta do dogma, pois o bloqueio emocional (que normalmente acontece em conflitos diretos) dificulta, ou mesmo impede, o pensamento crítico. Desta forma, a apresentação das inevitáveis contradições do dogma sobre a lógica racional deve seguir uma linha de discurso sutil, polida e bem construída (nunca ofensiva ou destrutiva).

Note que a questão aqui não é o absurdo das idéais prosposta pelos dogmas dos fundamentalistas religiosos, mas o sequinte exercício: "Como você poderia abordar estes indivídios, de forma delicada, a fim de desconstruir sua desinformação (minimizando seu o estrago) e ainda incutir nessas pessoas o pensamento crítico?". Note que o exercício desta dialética é realmente muito difícil!

Finalmente, vale lembrar que a própria existência das religiões, e do próprio comportamento social humano, é decorrente de mecanismos genéticos e da seleção natural, ou seja, não cabe a ciência a sua destruição, uma vez que é um processo natural em si, mas o seu esclarecimento e conciliação para fins de evolução do conhecimento e da própria humanidade.

quinta-feira, 24 de abril de 2008

[Artigo] A pseudociência nas universidades brasileiras

Eis um excelente artigo que, apesar de carente de citação, traz a tona uma questão de suma importância: a legitimação da pseudociência no país.

REIS, W.P., "A Pseudociência nas Universidades Brasileiras", Proceedings of Ibero-American Conference on Critical Thinking, Buenos Aires, 2005.

Read this doc on Scribd: A pseudociência nas universidades


O autor colabora com o Projeto Ockham e o blog "Dragão da Garagem", com excelentes discursões sobre pensamento crítico.

Particularmente, recomendo o último post "o problema das virgens" em que o mesmo autor discute a "questão das 27 virgens no paraíso mulçumano", trazendo uma pespectiva etimológica inusitada para a questão.

sexta-feira, 18 de abril de 2008

[Música] A última dança

Eis uma pérola do tempos da república! Como estou "de molho" estes dias, acabei por fazer esta pequena colagem no movie maker com alguns vídeos do youtube! Para o bom saudosismo de todos! Muita depressão e melancolia numa melodia simplesmente delicada (cortesia dos gênios Paulo e Alex). Grande abraço a todos e muitas saudades! [enjoy!]





A última dança

Os olhos da morte fitaram os meus de tao perto...
Eu fiquei sem jeito...
Toquei nos lábios dela com o dedo do silêncio.
Ao toque de lábios tão frios
Bailamos pela noite adentro
E de tão perdido, no vazio,
eu esqueci do tempo
E dos resquícios de vida que ainda havia em mim. . .
havia em mim. . .
havia em mim. . .
havia em mim. . .


Bailando com a morte no salão do mundo
Eu perco a vida de vista e me Afundo. . .
E me Afundo. . .
E me Afundo. . .

Letras: Paulo Rocha
Música: Alex "Brasil"
Arranjo: Porque Neuma!
Disco: A pergunta que não quer calar (2001) [independente]







sábado, 12 de abril de 2008

[Pessoal] Assim caminha a humanidade?

Eis um fato curioso. Fui a praia. Algumas vezes até.

Fui a praia. Banal por demais até.

Parece até poesia, mas não é.

Na areia haviam parasitas, onde enfiei meu pé.

Pois os parasitas (se chamavam "larvas migrans") infectaram a pele de bactérias, dai meu pé inchou no domingo passado (se chama "celulite": pra mim isso era coisa de perna de mulher, mas parece que é mais sério que isso). Fui no hospital e lá fiquei: foi internação na hora, pois a tal "celulite" pode evoluir para uma infecção generalizada (se chama "sepsis", e tem muita gente que morre disso!) e assim estive no Hospital São Mateus essa semana inteira (i.e. 5 dias com 2 antibióticos na veia). A dose era tão alta que me deixava grogue (eu não sabia que antibióticos faziam isso). Hoje tive alta (graças a Deus, pois não ia suportar outra punção na veia: tenho trauma disso e morro de medo - de tremer todo e bater os dentes - e pra piorar tive uma flebite de leve, mas esse foi o "açucar" desse caldo! Meu medo é da agulha! hehehehehe...).


A cima e ao lado seguem fotos tiradas já em casa, hoje de manhã, do meu pé "novo de novo" (acredite: está MUITO melhor). Em verdade, o perigo maior já passou (i.e. dessa eu não morro), mas como dá pra imaginar ainda vou ficar mais uns 15 dias literalmente "com o pé pra cima". Pior que até já desencanei e não estou ansioso com isso. Creio ser isto o mais importante dada a inevitabilidade do mundo (i.e. "relaxa e goza" hehehhee...).

Ademais, não produzi nada (nem fiz nada interessante) no hospital, pois a sonolência da medicação, o desamparo do desconhecimento da data de alta hospitalar e a "sombra constante" de novas punções na veia, meio que me desmotivaram (como dizia meu velho, "não tinha élan"*). Assim a única coisa que tenho para falar hoje é da minha própria história. hehehehehe... E sendo esta uma história, há de haver uma moral (ou várias morais, pois nenhuma é, em absoluto, absoluta...):
  • Use botas "sete léguas" na praia (ou pelo menos não enterre o pé na areia - o que era um discreto costume meu, enquanto tomava um cervejinha na praia - uma "viagem" do tipo "contato com a terra, com a natureza, etc.").
  • Não leve o cachorro para a praia, oh FDP! Ou pelo menos recolha os produtos finais!
  • Eu sou um "cara de sorte"! :D

Bem, parece que assim caminha a humanidade! Menos eu, que por enquanto, não vou a lugar algum! hehehehhehe...

* Uma pesquisa rápida na wikipédia esclarece que "élan vital" ou "força vital" foi uma teoria equivocada para a evolução, do começo do século passado, que atribuia a vida a um fluído mítico - sendo a base de inspiração para a "força" de Star Wars. Esperar o que do velhão: ele chama "cinema" de "cinemascope"! Falando nisso ele me trouxe umas muletas para usar esses dias. Brigadão pai, não sei o que faria sem você. Te amo!

sábado, 5 de abril de 2008

[Livro] Contos plausíveis


Segundo João Cabral de Melo Neto, "Carlos Drummond de Andrade mostra que ser poeta não significava ser sonhador, e que a ironia e a prosa cabem dentro da poesia". De acordo!

Este livro é realmente inusitado! Antes de mais nada, porque me foi emprestado pela Ana "da elétrica" ("da elétrica" não porque a ela fosse hiperativa, mas porque cursamos engenharia juntos na boa e velha universidade estadual de Campinas! Bons tempos!!!!) que reencontrei depois de muitos anos.

Mas o livro também "faz por onde". São contos curtos e independentes: cada um com menos de uma página (um chega a ter 3 linhas). Perfeito para quem não tem saco de ler textos longos! São extremamente divertidos, onde cada final garante uma boa risada. O texto exercita de forma primorosa dois princípios básicos: simplicidade e lógica (no meu entender, duas das grandes virtudes universais). Deveria ser usado nas escolas para ensinar redação e bom senso (isso se ensina?).

Verdade seja dia: o escencial é que me diverti demais lendo esse livro, o que foi um problema pois acabou quase imediatamente, me deixando "orfão". A grande "sacada" vem por conta de uma implícita crítica irônica à pretensão literária, com finais perfeitamente lógicos à situações fantasiosas (dái o nome contos plausíveis). Desta forma, clichês são satirizados com um final comum, gerando uma surpresa e uma boa risada. Na verdade, a identificação com o livro foi tão grande, em seu culto a simplicidade e racionalidade (i.e. simplicity is beautiful), que perguntei ao bom e velho google se o Drummond não era engenheiro. Mas era farmacêutico!

Para dar o gostinho, eis um conto plausível "pseudo-aleatório":


Abotoaduras


O maior fabricante de abotoaduras de punho fechou a indústria depois de convencer-se de que é infinitamente reduzido o número de camisas de manga comprida à disposição da humanidade. E, mais, que os exemplares deste gênero, ainda existentes, são providos de botões, dispensando abotoaduras.


– Trabalhei a vida inteira no setor – lastimava-se – e almejava legar a meus filhos a tradição das abotoaduras de punho, como requinte terminal de uma camisa digna desse nome. Os fatos ergueram-se contra mim. Não posso mais produzir abotoaduras de punho para camisas sem punho ou de punho abastardado por míseros botões de plástico.


Concluiu que é o fim da civilização e ia enforcar-se numa camisa esporte, estampada, quando esta, movida por vento súbito, saiu pelos ares, qual bandeira solta. E era tão bonito o esvoaçar do pano bigarreado, tão graciosas as evoluções que o homem resolveu desistir da morte e aplicar sua fortuna em uma indústria colossal de camisas de manga curta.

quinta-feira, 3 de abril de 2008

[.NET] Comunicador simples com .NET remoting


Os componentes das APIs .Net Remoting e Java RMI (Remote Method Invocation) provêm funcionalidades para desenvolvimento de sistemas distribuídos, permitindo a chamada remota de procedimentos (i.e. de forma semelhante à RPC, no entanto, sobre o paradigma OO). Neste contexo, os métodos invocados podem ser de processos distindos na mesma estação ou em estações diversas (i.e. máquinas virtuais distintas).

Tal tecnologia também é fundamental para a integração de sistemas, sendo um alternativa aos webservices em contextos onde a performance é crítica. De forma geral, a grande vantagem sobre um implementação a nível de sockets é que se dispensa o desenvolvimento de todo um protocolo de comunicação.
Para o desenvolvimento de uma aplicação cliente-servidor sobre estas APIs, deve-se definir a interface de serviços a serem oferecidos pelo objeto servidor. Os serviços especificados pela interface remota deverão ser implementados através de classes concretas. Com a interface estabelecida e o serviço implementado, é finalmente possível criar as aplicações cliente e servidor, sendo necessário apenas o registro do serviço para que as APIs citadas encapsulem toda a serialização e transferências das mensagens.

Neste artigo apresenta-se uma aplicação "chat" simples sobre a API .Net Remoting da plataforma .NET 2.0, onde diversos clientes contectam-se a um servidor para enviar mensagens aos outros participantes. Desta forma, a solução será dividada em 3 projetos:
  • assembly "MessagerCore" (DLL): contendo toda a lógica da aplicação.

  • aplicação console "MessagerServer": com responsabilidade de ser o mediador de mensagens e servidor da aplicação .Net Remoting

  • aplicação Windows Forms "MessagerClient": com responsabilidade de interface dos usuários e cliente .Net Remoting
Neste aspecto, inicia-se pelo desenvolvimento do assembly "MessagerCore" (DLL), através da definição das interfaces do serviço. Foram definidas as interfaces IChatter (representa o usuário cliente do chat) e IChatServer (representa o mediador de mensagens). Na interface IChatter, o método MessageArrived(string message) dispara o evento IncomingMessage para tratamento no cliente.

Interface IChatter
namespace net.jorgealbuquerque.Messager.Core
{
public interface IChatter
{
// Recupera o nome do usuário do chat
string Name { get; }
// Dispara o evento IncomingMessage para tratamento no cliente
void MessageArrived(string message);
}
}

Na interface IChatServer, os métodos RegisterChatter e UnRegisterChatter registram, respectivamente, o ingresso e abandono do cliente no chat. Por sua vez, o método AddMessage(IChatter sender, string message) representa o envio da mensagem.

Interface IChatServer
namespace net.jorgealbuquerque.Messager.Core
{
public interface IChatServer
{
// Ingressa no chat
void RegisterChatter(IChatter chatter);
// Abandona o chat
void UnRegisterChatter(IChatter chatter);
// Envia mensagem
void AddMessage(IChatter sender, string message);
}
}
Uma vez definidas as interfaces do serviço remoto, pode-se construir suas classes concretas. Nestes contexto, a classe Chatter (realização de IChatter) poderia ser implementada na assembly cliente e a classe ChatServer (realização de IChatServer) poderia ser implementada no servidor. No entanto, foi realizada a opção de manter toda a lógica no assembly "MessagerCore", de forma a prover apenas lógica de apresentação nas aplicações cliente e servidor. Tal escolha quebra um pouco as o design pattern observer, mas separa eficientemetne as camadas de negócios e apresentação. Observe, no entanto, que não existe nenhuma dependência entre os namespaces do cliente e servidor.

A classe Chatter estende MarshalByRefObject, o que permite a sua serialização e manipulação remota pela API .NET remoting. Note ainda que o método MessageArrived(string message) ativa o evento IncomingMessage.

Classe Chatter
using System;
using System.Runtime.Remoting;
using net.jorgealbuquerque.Messager.Core;
namespace net.jorgealbuquerque.Messager.Client
{
public class Chatter : MarshalByRefObject, IChatter
{
public Chatter(string name)
{
_Name = name;
}
public event IncomingMessageEventHandler IncomingMessage;
public delegate void IncomingMessageEventHandler(string Message);
private string _Name;
public string Name
{
get { return _Name; }
}
public void MessageArrived(string message)
{
if (this.IncomingMessage != null)
this.IncomingMessage(message);
}
}
}


De forma semelhante, o servidor também estende MarshalByRefObject. Neste contexto, o método UpdateObsevers(string message) realiza a atualização de todos os clientes, segundo o design pattern observer, por meio do evento IncomingMessage.


Classe ChatServer
using System;
using System.Runtime.Remoting;
using net.jorgealbuquerque.Messager.Core;
using System.Collections.Generic;
namespace net.jorgealbuquerque.Messager.Server
{
public class ChatServer : MarshalByRefObject, IChatServer
{
private List _Chatters = new List();
public override object InitializeLifetimeService()
{
return null;
}
private void UpdateObsevers(string message)
{
foreach (IChatter observer in _Chatters)
observer.MessageArrived(message);
//Console.WriteLine(message);
}
public void AddMessage(IChatter chatter, string message)
{
if (string.IsNullOrEmpty(message))
return;
UpdateObsevers(string.Format("{0}) {1} disse: {2}",
DateTime.Now.ToLongTimeString(),
chatter.Name, message));
}
public void RegisterChatter(IChatter chatter)
{
_Chatters.Add(chatter);
UpdateObsevers(
string.Format("{0}) Usuário {1} entrou.",
DateTime.Now.ToLongTimeString(), chatter.Name));
}
public void UnRegisterChatter(IChatter chatter)
{
_Chatters.Remove(chatter);
UpdateObsevers(
string.Format("{0}) Usuário {1} saiu.",
DateTime.Now.ToLongTimeString(), chatter.Name));
}
}
}
Finalmente, a lógica de registro e conexão dos servidor e cliente foram encapsulados nas classes de serviço abaixo. A classe ServerChannelService apresenta registro para intanciação remota do ChatServer como Singleton (i.e. apenas um mediator para todos os clientes). Por sua vez, a classe ClienteChannelService mostra a chamada remota propriamente dita da mesma classe pela construção Activator.GetObject().

Classe ServerChannelService
using System;
using System.Runtime.Remoting;
using net.jorgealbuquerque.Messager.Core;
namespace net.jorgealbuquerque.Messager.Server
{
public class ServerChannelService
{
public ServerChannelService(int port)
{
new RemotingTcpServicesHelper().ServerRegisterChannel("ChatServer", port);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ChatServer), "ChatServer", WellKnownObjectMode.Singleton);
}
}
}


Classe ClientChannelService
using System;
using net.jorgealbuquerque.Messager.Core;
namespace net.jorgealbuquerque.Messager.Client
{
public class ClientChannelService
{
IChatServer _ChatServer = null;
public ClientChannelService(string url, int port)
{
new RemotingTcpServicesHelper().ClientRegisterChannel("ChatServer", port);
string uri = string.Format("tcp://{0}:{1}/ChatServer", url, port);
_ChatServer = (IChatServer)Activator.GetObject(typeof(IChatServer), uri);
}
public IChatServer ChatServer
{
get { return _ChatServer; }
}
}
}

Ambos os serviços consomem a classe RemotingTcpServicesHelper, responsável pelo registro do canal de comunicação TCP entre servidor e cliente. No caso, esta implementação foi definida de forma a alterar o nível de segurança da instanciação de objetos remotos para "Full" (o padrão do framework 1.1 em diante é "low", o que impossibilita a troca de objetos serializados).

Classe RemotingTcpServicesHelper
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Channels;
using System.Runtime.Serialization.Formatters;
using System.Collections;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting;
using net.jorgealbuquerque.Messager.Server;
namespace net.jorgealbuquerque.Messager.Core
{
public class RemotingTcpServicesHelper
{
public void ClientRegisterChannel(string serviceName, int port)
{
if (ChannelServices.GetChannel(serviceName) != null)
return;
IDictionary props = new Hashtable();
props["name"] = serviceName;
props["port"] = 0;
props["bindTo"] = "127.0.0.1";
props["typeFilterLevel"] = TypeFilterLevel.Full;
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
BinaryClientFormatterSinkProvider clientprovider = new BinaryClientFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
TcpChannel channel = new TcpChannel(props, clientprovider, serverProvider);
ChannelServices.RegisterChannel(channel, false);
}
public void ServerRegisterChannel(string serviceName, int port)
{
if (ChannelServices.GetChannel(serviceName) != null)
return;
IDictionary props = new Hashtable();
props["name"] = serviceName;
props["port"] = port;
props["bindTo"] = "127.0.0.1";
props["typeFilterLevel"] = TypeFilterLevel.Full;
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
TcpChannel channel = new TcpChannel(props, null, serverProvider);
ChannelServices.RegisterChannel(channel, false);
}
}
}

Finalmente, encerra-se o desenvolvimento do assembly "MessagerCore", passando-se a construção da aplicação servidora pelo projeto console "MessagerServer":

MessagerServer
using System;
using System.Collections.Generic;
using net.jorgealbuquerque.Messager.Server;
namespace net.jorgealbuquerque.Messager.MessagerServer
{
class Program
{
static void Main(string[] args)
{
int port = 8080;
ServerChannelService service = new ServerChannelService(port);
Console.WriteLine("O servidor de mensagens está ativo na porta {0}", port);
Console.WriteLine("Pressine enter para parar o servidor...");
Console.ReadLine();
}
}
}

Observa-se a total abstração da lógica de negócios na camada de apresentação do servidor. Da mesma forma, apresenta-se o código fonte do único formulário (frmMain) para a aplicação cliente Windows Forms.

frmMain.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using net.jorgealbuquerque.Messager.Core;
using net.jorgealbuquerque.Messager.Client;
using System.Threading;
namespace net.jorgealbuquerque.Messager.MessagerClient
{
public partial class frmMain : Form
{
private Chatter _Chatter;
private IChatServer _ChatServer;
public frmMain()
{
InitializeComponent();
}
private void InitServer()
{
_Chatter = new Chatter(txtName.Text);
ClientChannelService service = new ClientChannelService(
txtServerHost.Text,
Int32.Parse(txtServerPort.Text));
_ChatServer = service.ChatServer;
_Chatter.IncomingMessage += new
Chatter.IncomingMessageEventHandler(Chatter_IncomingMessage);
}
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
if (_ChatServer != null)
_ChatServer.UnRegisterChatter(_Chatter);
}
catch
{
this.Dispose();
}
}
private void SendMessageToServer()
{
string texto = txtMessage.Text;
txtMessage.Text = string.Empty;
if (_ChatServer == null _Chatter == null string.IsNullOrEmpty(texto))
return;
_ChatServer.AddMessage(_Chatter, texto);
}
private void Chatter_IncomingMessage(string Message)
{
txtChat.Text = txtChat.Text + Message + "\r\n";
}
private void Send()
{
try
{
new Thread(new ThreadStart(this.SendMessageToServer)).Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Register()
{
if (string.IsNullOrEmpty(txtName.Text))
{
MessageBox.Show("Nome inválido!", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
try
{
InitServer();
_ChatServer.RegisterChatter(_Chatter);
pnlLogin.Visible = false;
pnlMain.Visible = true;
this.Text = string.Format("Usuário: {0}", txtName.Text);
}
catch (System.Net.Sockets.SocketException exSocket)
{
MessageBox.Show("Servidor não encontrado: " + exSocket.ErrorCode, "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnSend_Click(object sender, EventArgs e)
{
Send();
}
private void txtMessage_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == Convert.ToChar(13))
Send();
}
private void btnRegister_Click(object sender, EventArgs e)
{
Register();
}
private void txtName_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == Convert.ToChar(13))
Register();
}
}
}

O formulário foi divido em 2 panels (pnlLogin e pnlMain), responsáveis pelo login no servidor e pela interface principal do chat. Finalmente, apresenta-se o construtor da aplicação, onde é definido a flag CheckForIllegalCrossThreadCalls = false. Tal opção permite a chamada dos componentes do formulário por múltiplas threads.

Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace net.jorgealbuquerque.Messager.MessagerClient
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
frmMain.CheckForIllegalCrossThreadCalls = false;
Application.Run(new frmMain());
}
}
}

domingo, 30 de março de 2008

[ASP.NET] Componentizando o upload de imagens na camada de negócios

Um problema usual em aplicações ASP.NET é o envio de imagens. Esta necessidade é plenamente suprida pelo componente nativo ASP.NET FileUpload. Tal "WebControl", apresenta um TextBox e um botão "Browse", que encapsula a manipulação da stream da imagem durante o PostBack.

A figura ao lado apresenta-se um aplicação "mock" para envio de imagens. Abaixo segue o código em C# para o delegate do evento OnClick() do botão “Enviar”.

protected void btnEnviar_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
// Recupera dados da imagem
string fileName = FileUpload1.FileName;
string filePath = Page.MapPath("~") + Path.DirectorySeparatorChar +
"images" + Path.DirectorySeparatorChar + fileName;
byte[] data = FileUpload1.FileBytes;
// Escreve a imagem em disco
FileStream writer = new FileStream(filePath, FileMode.Create);
writer.Write(data, 0, data.Length);
writer.Close();
// Apresenta a imagem salva na tela
lblFileName.Text = "Arquivo enviado: " + fileName;
Image1.ImageUrl = "~/images/" + fileName;
Image1.Visible = true;
}
else
{
lblFileName.Text = string.Empty;
Image1.Visible = false;
}
}


No entanto, pode-se criticar esta implementação por quebrar a separação em camadas, uma vez que a lógica de manipulação da imagem está inserida no modelo da camada de apresentação (o arquivo .aspx.cs representa o model MVC). Desta forma se propõe o serviço abaixo a ser encapsulado em um assembly (dll) a parte para fins de reuso.

interface IUploader

using System;
namespace net.jorgealbuquerque.Services.Uploader
{
public interface IUploader
{
string BaseDir { get; set; }
System.Drawing.Size ThurmbnailSize { get; set; }
bool CreateThumbnails { get; set; }
string GetImageDirectory();
void Upload(string fileName, System.IO.Stream data);
void Upload(string fileName, byte[] data);
void Delete(string fileName);
void CreateThumbnail(string fileName);
}
}


classe UploaderService
using System;
using System.IO;
namespace net.jorgealbuquerque.Services.Uploader
{
public class UploaderService: IUploader
{
private bool _CreateThumbnails = true;
public bool CreateThumbnails
{
get { return _CreateThumbnails; }
set { _CreateThumbnails = value; }
}
private Size _ThurmbnailSize = new Size(64, 64);
public Size ThurmbnailSize
{
get { return _ThurmbnailSize; }
set
{
Size size = value;
if (size == null)
throw new ArgumentNullException("O tamanho do thurmbnail não pode ser nulo.");
if (size.Height < 5 || size.Width < 5)
throw new ArgumentException("O tamanho do thurmbnail inválido!");
_ThurmbnailSize = size;
}
}
private string _baseDir = "images";
public string BaseDir
{
get { return _baseDir; }
set
{
string dir = value;
if (string.IsNullOrEmpty(dir))
throw new ArgumentException("Nome de diretório base inválido!");
dir = dir.Replace('/', ' ');
dir = dir.Replace('\\', ' ');
dir = dir.Trim();
_baseDir = dir;
}
}
public string GetImageDirectory()
{
return AppDomain.CurrentDomain.BaseDirectory +
_baseDir + Path.DirectorySeparatorChar;
}
protected void Validate(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentException("nome de arquivo inválido!");
string ext = Path.GetExtension(fileName).ToLower();
if (ext != ".jpg" && ext != ".gif" &&
ext != ".bmp" && ext != ".png" && ext != ".tif")
throw new ArgumentException("extensão de arquivo inválida!");
string dir = GetImageDirectory();
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
}
public void Upload(string fileName, byte[] data)
{
Validate(fileName);
FileStream writer = new FileStream(GetImageDirectory() + fileName,
FileMode.Create);
writer.Write(data, 0, data.Length);
writer.Close();
if (_CreateThumbnails)
CreateThumbnail(fileName);
}
public void Upload(string fileName, Stream data)
{
byte[] buffer = new byte[data.Length];
data.Write(buffer, 0, (int)data.Length);
Upload(fileName, buffer);
buffer = null;
GC.SuppressFinalize(buffer);
if (_CreateThumbnails)
CreateThumbnail(fileName);
}
public void Delete(string fileName)
{
string filePath = GetImageDirectory() + fileName;
if (File.Exists(filePath))
File.Delete(filePath);
string thumbnailPath = GetImageDirectory() + "thumb_" + fileName;
if (File.Exists(thumbnailPath))
File.Delete(thumbnailPath);
}
public void CreateThumbnail(string fileName)
{
string thumbnailName = "thumb_" + fileName;
string filePath = GetImageDirectory() + fileName;
string thumbnailPath = GetImageDirectory() + thumbnailName;
System.Drawing.Image img = System.Drawing.Image.FromFile(filePath);
System.Drawing.Image thumbnail = img.GetThumbnailImage(
_ThurmbnailSize.Width,
_ThurmbnailSize.Height,
new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback),
IntPtr.Zero);
thumbnail.Save(thumbnailPath);
}
protected bool ThumbnailCallback()
{
return true;
}
}
}

Finalmente, sugere-se uma fábrica concreta que permita um ponto de junção para a evolução do componente:

classe UploaderServiceFactory

using System;
namespace net.jorgealbuquerque.Services.Uploader
{
public class UploaderServiceFactory
{
private UploaderServiceFactory() { }
private static UploaderServiceFactory _instance;
public static UploaderServiceFactory GetInstance()
{
if (_instance == null)
_instance = new UploaderServiceFactory();
return _instance;
}
public IUploader GetUploader()
{
return new UploaderService();
}
}
}



Desta forma, o delegate do evento OnClick() seria reescrito da forma abaixo. Observa-se a separação entre lógica de negócios e apresentação.

protected void btnEnviar_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
UploaderServiceFactory().GetInstance().GetUploader().
Upload(FileUpload1.FileName, FileUpload1.FileBytes);
lblFileName.Text = "Arquivo enviado: " + FileUpload1.FileName;
Image1.ImageUrl = "~/imagens/" + FileUpload1.FileName;
Image1.Visible = true;
}
else
{
lblFileName.Text = string.Empty;
Image1.Visible = false;
}
}

Note ainda que o componente pode ser igualmente empregado em contexto Windows Forms sem qualquer adaptação, ampliando seu potencial de reuso. De forma a tornar mais evidente a separação em camadas obtida pelo componente descrito, propõe-se a persistência do cadastro de imagens. Neste contexto, definiu-se a camada de persistência conforme a figura ao lado através de um componente DataSet "tipado", de acordo com o modelo proposto pela Microsoft. Por sua vez, o serviço da camada de negócios também segue as recomendações da empresa, sendo apresentado abaixo:

using System;
using System.IO;
using System.Collections.Generic;
using System.Web.UI.WebControls;
using System.ComponentModel;
using net.jorgealbuquerque.Sample.Dao;
using net.jorgealbuquerque.Sample.Dao.DataSet1TableAdapters;
using net.jorgealbuquerque.Services.Uploader;
namespace net.jorgealbuquerque.Sample.Services
{
[System.ComponentModel.DataObject]
public class ManterImagesService
{
private static ImageDAO _DAO = null;
protected ImageDAO DAO
{
get
{
if (_DAO == null)
_DAO = new ImageDAO();
return _DAO;
}
}
public void Upload(FileUpload fileUpload, string comentario)
{
UploaderServiceFactory.GetInstance().GetUploader().
Upload(fileUpload.FileName, fileUpload.FileBytes);
Insert(fileUpload.FileName, DateTime.Now, comentario);
}
public void Upload(byte[] imageStream, string nome, string comentario)
{
UploaderServiceFactory.GetInstance().GetUploader().
Upload(nome, imageStream);
Insert(nome, DateTime.Now, comentario);
}
public void Upload(Stream imageStream, string nome, string comentario)
{
UploaderServiceFactory.GetInstance().GetUploader().
Upload(nome, imageStream);
Insert(nome, DateTime.Now, comentario);
}
[System.ComponentModel.DataObjectMethod(DataObjectMethodType.Select, true)]
public DataSetLisier.ImageDataTable GetAll()
{
return DAO.GetAll();
}
[System.ComponentModel.DataObjectMethod(DataObjectMethodType.Select, false)]
public DataSetLisier.ImageDataTable GetByID(int ID)
{
if (ID < -1)
throw new ArgumentException("ID inválido!");
return DAO.GetByID(ID);
}
public DataSetLisier.ImageRow GetRowByID(int ID)
{
DataSetLisier.ImageDataTable tbl = GetByID(ID);
if (tbl.Count != 1)
throw new ArgumentException("Imagem não encontrada!");
return (DataSetLisier.ImageRow)tbl.Rows[0];
}
[System.ComponentModel.DataObjectMethod(DataObjectMethodType.Delete, true)]
public void Delete(int ID)
{
if (ID < 0)
throw new ArgumentException("ID inválido!");
DataSetLisier.ImageRow img = GetRowByID(ID);
if (img == null)
throw new ArgumentException("imagem não cadastrada!");
UploaderServiceFactory.GetInstance().GetUploader().Delete(img.nome);
int linhas = DAO.Delete(ID);
if (linhas != 1)
throw new OperationCanceledException("Erro excluindo imagem!");
}
protected void ValidaDados(string nome, DateTime data, string comentario)
{
if (string.IsNullOrEmpty(nome))
throw new ArgumentException("nome de arquivo inválido!");
}
[System.ComponentModel.DataObjectMethod(DataObjectMethodType.Insert, true)]
public void Insert(string nome, DateTime data, string comentario)
{
ValidaDados(nome, data, comentario);
int linhas = DAO.Insert(nome, data, comentario);
if (linhas != 1)
throw new OperationCanceledException("Erro inserindo imagem!");
}
[System.ComponentModel.DataObjectMethod(DataObjectMethodType.Insert, false)]
public void Insert(DataSetLisier.ImageRow row)
{
if (row == null)
throw new OperationCanceledException("Imagem não pode ser nula!");
Insert(row.nome, row.data, row.comentario);
}
[System.ComponentModel.DataObjectMethod(DataObjectMethodType.Update, true)]
public void Update(string nome, DateTime data, string comentario, int ID)
{
// Regra de negocio: nome e data da imagem
// nao podem ser alterados pela interface!
DataSetLisier.ImageRow row = GetRowByID(ID);
if (row == null)
throw new OperationCanceledException("Imagem não cadastrada!");
int linhas = DAO.Update(row.nome, row.data, comentario, ID);
if (linhas != 1)
throw new OperationCanceledException("Erro alterando imagem!");
}
[System.ComponentModel.DataObjectMethod(DataObjectMethodType.Update, false)]
public void Update(DataSetLisier.ImageRow row)
{
if (row == null)
throw new OperationCanceledException("A imagem não pode ser nula!");
ValidaDados(row.nome, row.data, row.comentario);
int linhas = DAO.Update(row);
if (linhas != 1)
throw new OperationCanceledException("Erro alterando imagem!");
}
}
}

Vale resaltar novamente que a classe de serviço descrita acima deve ser encapsulada em um assembly (dll) a parte, sendo plenamente compatível com aplicações Windows Forms, o que demonstra a separação em camadas. Desta, pode-se evoluir o mock inicial para a pequena aplicação abaixo.



Onde novo delegate do botão "Enviar" é apresentado abaixo, onde fica ainda mais evidente a separação em camadas entre as lógicas de apresentação, negócios e persistência:


protected void btnEnviar_Click(object sender, EventArgs e)
{
new ManterImagesService().Upload(FileUploader1, txtComentario.Text);
ImageGridView.DataBind();
}