Arquivo para Agosto, 2008

Servidor de Aplicação em .NET

A arquitetura em camadas tem sido amplamente adotada pelos analistas e desenvolvedores de sistemas computacionais. Isso porque esta arquitetura favorece a aplicação de vários conceitos envolvendo a engenharia de software como, por exemplo, modularidade, manutenibilidade, etc. O conceito que motivou a escrita deste post é a escalabilidade, ou seja, o desafio de construir sistemas preparados para “crescer”. Um sistema e dito escalável, por exemplo, quando seu desempenho aumenta com acréscimo de hardware. Não estou falando em ficar mais rápido por que agora ele está rodando em um servidor mais rápido do que o antigo. Estou falando em potencial de crescimento e adaptação a cenários de produção. Imagine a situação: Uma pequena empresa com poucos recursos financeiros requisitou da equipe de TI um sistema de gestão empresarial. Após levantamento de requisitos, avaliação das condições de infraestrutura, decidiu-se pela seguinte configuração: arquitetura cliente/servidor. Um servidor de banco de dados foi colocado na rede e a aplicação cliente faria solicitações a este servidor. A aplicação cliente implementava todas as regras de negócio do sistema de gestão e o servidor de banco de dados serviria apenas como repositório de dados. A princípio tudo funcionava bem: LAN com poucos usuários, poucos dados no banco… Mas a empresa cresceu. Mais usuários na rede, maior volume de dados, novas filiais, negócios via internet, novas regras de negócio, etc. Já está claro que o velho sistema precisaria ser reescrito. Talvez até em outra plataforma de desenvolvimento. Sem falar que, possivelmente, novos especialistas em TI precisarão ser contratados ou, pelo menos, os anteriores terão que passar por algum treinamento/reciclagem. Tudo isso porque não se pensou na possibilidade do sistema crescer em necessidades de regras de negócio juntamente com a necessidade de infraestrutura. A pequena empresa, hoje, tem condições para investir em mais computadores-servidores, internet, redes de longa distância. Ela tem alto volume de dados que precisam ser tratados com velocidade dentro e fora do ambiente da empresa. Eu pergunto: é qualquer sistema que pode se adaptar a esta nova realidade? O grande desafio é CONSTRUIR SISTEMAS PREPARADOS PRA CRESCER. O sistema não precisa nascer grande mas deve possuir características que, com pequenas adaptações, tenha seu potencial aumentado.

Um bom começo para quem prentende trabalhar com escalabilidade é verificar se seu sistema admite a possibilidade de trabalhar utilizando um servidor de aplicação: as aplicações clientes (desktop ou web) fazem requisições a um servidor. Neste servidor estão implementados todas as regras de negócio e esse servidor faz acesso ao banco de dados para, então, responder às solicitações. Para utilizar essa proposta, que modificações voce teria que fazer no seu sistema?

  1. Teria que reescrever o sistema em outra plataforma?
  2. Teria que efetuar algumas mudanças sim. Voce acha que dá pra fazer? Em quanto tempo?
  3. Teria que, primeiro ser treinado em ambiente em camadas pra depois pensar nisso?
  4. Teria que, primeiro mudar sua forma de programar?
  5. Teria que colocar as regras de negócio da aplicação em um só lugar para depois pensar no servidor de aplicação?
  6. Teria que modelar o sistema primeiro em orientação a objetos?
  7. Outro “teria” qualquer….

Veja que parece simples responder a esta pergunta mas não é tão imediato assim. Nem todos os sistemas são preparados para isso efetivamente. Outras respostas:

  1. Construimos o sistema com esta visão. Foi previsto que o projeto teria grande possibilidade de crescimento tanto em recursos quanto em necessidades.
  2. Sim. É só modificar o arquivo de configurações do sistema para que as aplicações clientes façam solicitações para o servidor.
  3. Sim. Só preciso modificar uma linha do sistema, recompila-lo e recoloca-lo em produção.
  4. Quantos servidores posso utilizar? Pelo menos um servidor para DAO´s e banco de dados e um outro servidor de regra. Poderiam ser mais servidores de aplicação espalhados pela rede. Depende apenas do nível de resposta que a empresa pretende ter e da disponibilidade de recursos físicos.
  5. O sistema permite dividir papéis facilmente.
  6. Outros “só se for agora” qualquer….

Se voce puder utilizar alguma dessas respostas, parabéns. Voce já está no caminho certo. É essa linha que a engenharia de software moderna quer que os novos sistemas sigam. Para aqueles que aindam não perseguem estes objetivos vou tentar passar algumas dicas como roteiro inicial a seguir.

Eu utilizava .NET 1.1 quando me fiz a seguinte pergunta: Em J2EE, pode-se configurar a utilização de um servidor de aplicação através dos EJB containers. JBoss é um exemplo de servidor de aplicação gratuito em Java. Qual o servidor de aplicação para .NET??? Há um tempo atrás fui a uma apresentação sobre .NET 2.0 com Visutal Studio 2005. Na saída fiz essa pergunta ao facilitador e ele me respondeu com uma palavra: “Remoting”. Ao chegar em casa me dediquei a pesquisar sobre o assunto. A seguir, deixo indicado um artigo que serviu de base para nossa trajetória sobre o assunto:

.NET Remoting – Parte 1 – Acessando informações remotamente
.NET Remoting – Parte 2 – Acessando informações remotamente

Desde então temos trabalhado para que a construção dos nossos sistemas tenham carateríscas que viabilizem a arquitetura de acesso remoto. As pessoas que acompanharam em nosso blog os post entitulados NHIbernate + Visual Studio: Construindo aplicações em camadas devem ter notado em algumas partes dos códigos e da explanação a presença de flags para utilização de servidor de aplicação. Desenvolvemos uma solução chamada RemoteServer.NET em Visual Studio 2005 com 3 projetos que casa perfeitamente com esses indicadores:

  1. RemoteServerWin: Um servidor de aplicação utilizando .NET Remoting que roda como aplicação do windows.
  2. RemoteServerService: Um servidor de aplicação, também utilizando .NET Remoting que roda como serviço do windows.
  3. RemoteServerSetup: Um instalador dos projetos anteriores.

Vamos entender o esquema abaixo numa proposta didática, porém bem próxima de uma arquitetura ideal:

No ínicio do processo temos um usuário utilizando o seu computador para fazer solicitações ao servidor web através de um navegador. O servidor web é responsável apenas pela apresentação e desenho da página web. Qualquer necessidade de processamento de regras de négocio deverá ser enviada ao servidor de negócio. A linha usuario-navegador-servidor web-servidor de negócio poderia ser subistituida por outras configurações: celular-servidor web-servidor de negócio, aplicativo windows-servidor de negócio, etc. O fato é que o servidor de negócio precisa está preparado para receber e responder a requisições remotas. Neste servidor estaria configurado o RemoteServer.NET para responder solicitações da camada de apresentação. Algumas vezes o servidor de aplicação precisaria acessar dados do banco de dados. Esse acesso, de acordo com padrões de projetos e arquitetura em camadas deveria ser através de DAO´s. Logo poderiamos ter um servidor DAO para responder requisições feitas pelo servidor de negócio. Neste servidor DAO também teria instalado o RemoteServer.NET para responder requisições remotas. Por fim, temos uma máquina como servidor de banco de dados para acesso a dados.

As possibilidades são inúmeras. Imagine um enorme volume de usuários fazendo acesso ao servidor de regra. Poderiamos colocar novos servidores de negócio pra dividir a carga ou mesmo, mais servidores de acesso a dados fazendo com que as requisições fossem aleatórias a estes servidores evitanto o engarrafamento de requisições no mesmo servidor. Poderiamos também fazer replicação sincrona de dados no banco ou mesmo, utilização de cluster para dividir a carga de acesso ao banco de dados. Tudo dependendo do potencial do parque de informática da empresa e necessidade de acesso rápido ás informações. Adicionemos a tudo isso uma dose de XML Web Service para que outra aplicações possam conectar-se diretamente a seu servidor de regras via web.

As aplicações que voce desenvolve suporta essas possibilidades?  Isso é ESCALABILIDADE.

Na nossa página de Sistemas c/ Fontes estamos disponbilizando a solução RemoteServer.NET para aqueles que desejarem adquirir e ampliar seus horizontes. Vale lembrar que o RemoteServer.NET sozinho não faz milagre. A aplicação como um todo deve ser pensada para suportar esses requisitos. O primeiro passo é repensar seus conceitos em desenvolvimento e pesquisar soluções que sejam multi-utilizáveis.

Quem desejar adiquirir esse e outros produtos entre em contato. Teremos o maior prazer em responder. Voce estará contribuindo para manter nossas pesquisas bem como, a qualidade das nossas publicações.

É isso.

Visual Studio + NHibernate: Construindo aplicações em camadas – (cont. VI)

No post anterior criamos as classes que vão dar suporte às regras de negócio da nossa aplicação. Neste post, que é o último desta série, vamos criar a nossa camada de apresentação. Não pretendo aqui esgotar o assunto sobre isso, nem mesmo desenvolver todas as telas do sistema. O que iremos fazer é dá uma idéia inicial sobre a comunicação entre as camadas e criar uma página web que possa servir de base para o trabalho do desenvolvedor.

Até o momento tudo que fizemos foi puramente conceitual. E, de posse dos conceitos, utilizamos scripts para gerar os códigos. Destaque para algumas bibliotecas que encapsulam funcionalidades ligadas ao Hibernate, visando diminuir codificação e ganho em reaproveitamento de código. Daqui pra frente quem manda é a critatividade e a prática do programador para desenvolver a aplicação de acordo com os requisitos de sistemas definidos no projeto. Eu considero que é deste ponto que se inicia o trabalho de codificação efetivamente, visto que, até o momento, utilizamos geradores de código para criar as camadas que darão suporte a nossa aplicação. Eu utilizo essa prática em todo sistema corporativo que desenvolvo. Claro que a medida que vão surgindo outras necessidades, tanto as bibliotecas, quanto os scripts vão sofrendo adaptações. Por exemplo, desde quando comecei a escrever esta série algumas mudanças foram feitas nos scripts bem como na biblioteca. Isso é fácil de entender: a medida que vamos trabalhando, vamos aprendendo e aprimorando nossos hábitos e conceitos. Isso faz parte do ser humano: fazer hoje, melhor que ontém.

Então vamos iniciar os trabalhos. Abra a nossa solução no Visual Studio e adicione um novo projeto. Uma vez me perguntaram se este projeto poderia ser em windows forms. Claro que sim. Pode ser qualquer projeto de GUI: web, windows forms ou console. A camada de apresentação é o local onde o usuário final irá interagir com seu sistema. Quem define isso é o documento de requisitos. No nosso caso será um projeto web. Adicione um web site ou um web application. Eu prefiro utilizar web application e com suporte a AJAX. Qual a diferença? Bom… Eu começei no VS 2003 e ele já utilizava o conceito de web application. Eu gostava porque os arquivos .cs não precisavam ser colocados no site em produção. Ele gerava uma única .dll que implementava todas as classes. No web site, temos que colocar em produção os arquivos .cs que serão compilados, gerando as dll’s, em tempo de execução. Vantagem e desvantagem: Na web application qualquer modificação precisa ser recompilada. No web site modificações podem ser feitas diretamente no arquivo em produção. A compilação será feita automaticamente. Por outro lado, na web application tem como evitar a ação de pessoas não autorizadas nos códigos-fontes da  sua aplicação, enquanto no web site não tem como impedir diretamente. Teria que ser via sistama operacional. Enfim, veja o que é melhor. Fiquei com web application… Adicionei projeto web application com nome “AgendaTelefonica”. O Visual Studio gerou os arquivos.

Primeira coisa a fazer é configurar o Hibernate. Vamos utilizar o web.config para isso. Essa configuração vai dizer ao NHibernate qual banco de dados estamos utilizando e onde ele está (usuario, senha, dialeto, tipo de banco, etc.). Uma outra informação que eu coloco neste arquivo é o nome do assembly O/R. Quem vai ler esta informação é a bilbioteca Regisoft.Camadas.dll para configurar o mapeamento do Hibernate.  Neste arquivo, em nossa aplicação e relacionado ao Hibernate, não mexeremos mais neste arquivo. A menos que desejemos fazer alguma mudança ou adicionar algum recurso novo, como por exemplo, se desejarmos trabalhar como servidor remoto. Mas isso é assunto de outros posts.

Precisamos adicionar algumas referências em nossa aplicação web. Clique no botão direito do mouse sobre o nome do projeto que acabamos de adicionar e selecione “Add Reference…”. Selecione a guia project e, nessa guia, selecione os projetos BO, Interface e OR (não selecione o DAO) e clique em ‘OK’.. Lembre da obediência a hierarquia em cadamas que discutimos anteriormente. A camada de apresentação só pode fazer requisições à camada de negócio. Nunca à camada DAO. Lembra da biblioteca Regisoft.dll? Adicione ela também. Mas duas bibliotecas serão necessárias: Regisoft.web.dll e Regisoft.Web.UI.WebControls. A primeira possui recursos para manipulação de javascript, limpeza automática de campos da tela, atribuição automática de valores dos objetos O/R a campos da tela, etc. A segunda são alguns componentes da toolbox que eu adicionei alguns novos recursos.  Como voce já sabe, todas essas bibliotecas são encontradas na pasta ‘Bibliotecas‘ no local onde voce instalou o RSClass.

Para quem tiver alguma dúvida, o arquivo web.config pode ser encontrado no site do tutorial (http://tutorialagendatelefonica.googlecode.com). A Regisoft.Web.UI.WebControls.dll deve ser adicionada também na Toolbox do Visual Studio para facilitar. Para fazer isso clique com botão direito do mouse em qualquer lugar da Toolbox e selecione “Add Tab“. Dê um nome: “Regisoft”. Clique ainda com botão direito dentro da tab criada e selecione “Choose Items…“. Quando a janela aparecer clique em “Browse” e localize a biblioteca Regisoft.Web.UI.WebControls.dll (na pasta ‘Bibliotecas’ no local de instalação do RSClas. Confirme… Pronto! Os componentes foram adicionados. Abra a página Default.aspx e veja que na tab “Regisoft” possui alguns componentes. Inclusive essa é a maneira de adicionar webcontrols na Toolbox. Toda vez que voce baixar um componente visual pode adicioná-lo na toolbox dessa forma – Dica.NET ;)

Mas algumas considerações na configuração do NHibernate. No web.config há informações sobre o banco de dados (driver, dialect, conecctionstring, etc.). Como estamos utilizando o Firebird precisamos adicionar o driver do banco ao sistema. Clique no projeto web com botão direito do mouse e selecione ‘Add Reference…‘. Localize o arquivo FirebirdSql.Data.FirebirdClient.dll que está na pasta ‘Bibliotecas’ no local de instalação do RSClass e adicione ao projeto. Outro arquivo que também terá que adicionar ao projeto web é o NHibernate.ByteCode.Castle.dll. Nessa nova versão do NHibernate ele precisa desse arquivo. Ainda não li sobre a utilidade. Mas devemos adicionar se não o NHibernate não irá funcionar. Quem souber da utilidade deste arquivo e quiser contribuir eu agradeço.

Se tudo foi feito corretamente teremos a seguinte tela (mais ou menos assim):

Já estamos com nosso ambiente preparado para iniciar os trabalhos. Vamos criar nossa primeira página web. Faremos uma página para inclusão de UF. Há quem ache que não precisa. Basta popular o banco de dados com essas informações, já que as UF não mudam constantemente. Eu concordo. Mas vamos construir para fins didáticos. Pra não perder a prática utilizaremos o RSClass pra facilitar o trabalho. Não iremos nos preocupar com aparência da página fica como exercício para os interessados.

Como não é uma tabela com muitos dados vamos gerar como tabela simples. No RSClass acesse o menu Webform->Tabela. Configure como mostrado abaixo.

gerar_webformtabela

Algumas informações importantes sobre essa geração de páginas:

  1. Na pasta destino voce pode selecionar o local do deu projeto onde quer que seja gerado os arquivos. Pode ser na principal ou em subpastas do seu projeto;
  2. Atenção para isso: o título da página deve ser informado sem acentuação e cedilhas. Os acentos só devem ser colocados quando a página for incluída no projeto e convertida em web application. Veremos isso mais tarde;
  3. No campo arquivo é o nome do arquivo a ser gerado. Não coloque extensão. O próprio RSClass irá fazer isso.
  4. A namespace é formada pelo nome do projeto (no nosso caso AgendaTelefonica) e pelas pastas internas onde o arquivo será gerado separado por ponto(.). Exemplo se voce gerar dentro de um projeto Estoque na pasta interna Almoxarifado a namespace deve ser Estoque.Almoxarifado;
  5. Selecione a tabela;
  6. Se voce pretende usar uma master page na página geraada marque a última caixa. A página será gerada com estrutura de WebForm Content. No nosso exemplo não estamos utilizando.

No Visual Studio, clique em “Show All Files“, selecione os dois arquivos gerados, clique em um deles com o botão direito do mouse e selecione “Include In Project“. Agora, clique em UnidadesFederacao.aspx com o botão direito do mouse e selecione “Convert to Web Application“.

Vamos gerar agora a classe de acesso aos BO´s. Como já dissemos anteriormente vamos utilizar uma arquitetura em camadas sendo que os acessos aos componentes da camada inferior serão feitos por uma única via. Abra o RSClass e no menu Gerar->BOAccess.cs gere o arquivo.  Usando o mesmo procedimento do parágrafo acima inclua o arquivo no projeto (Include in Project).

Defina a página UnidadesFederacao.aspx como “Set As Start Page” e execute a aplicação. Vamos ver se funciona…

Pra variar, PROBLEMAS!!!!! Vamos as correções:

  1. Normalmente eu crio sistema com controle de acessos. E este, como foi definido nos requisitos inicias, também deve ter. Deveriamos ter feito primeiro o controle de acesso ao sistema num tela de login. Mas como o objetivo é didático pulamos esta etapa. Então vamos comentar algumas lihas da nossa primeira página. No método protected void Page_Load(object sender, EventArgs e) da página comente as linhas if (u == null) e
    Response.Redirect(“~/Index.aspx”); Voce sabe. Comentar é adicionar duas barras (//) na frente da linha.
  2. Eu também constumo desenvolver sistemas que eu chamo multi-empresa. São sistemas que podem ser utilizados por várias empresa ao mesmo tempo. Uma sem acessar os dados da outra. Nesse caso no cadastro do usuário é definido a que empresa ele pertence. Assim no momento das listagens e outra operações é informado ao sistema a empresa do usuário e as operações só serão executadas em dados dessa empresa. Esse não é o nosso caso. Então localize no sistema a o método protected DataTable getDataTable(). Altere a linha IList<AgendaTelefonica.OR.Uf> lst = BOAccess.getBOFactory().UfBO().Listar(u.IdEmpresa) para IList<AgendaTelefonica.OR.Uf> lst = BOAccess.getBOFactory().UfBO().Listar(“Sigla”). Veja que a única coisa que mudou foi o parâmetro do método ‘Listar‘ que era ‘u.IdEmpresa‘ e passou a ser ‘Sigla‘.

Depois dessas modificações a página já pode ser testada. Note e a página é bem simples e complatemente funcional. Seria interessante adicionar alguns recursos visuais com CSS ou coisas do tipo. Mas não é objetivo deste material.

A verdade é que os geradores de código não resolvem todos os problemas. Mas adiantam bastantes. Com algumas modificações podemos ter ótimos resultados. Imagine se tívessemos que escrever tudo do zero…

Vamos agora ilustrar a adição de métodos de acesso a dados no DAO. Abra o UfDAO.cs e adicione as seguintes linhas:

public Uf SelecionarPorSigla(string sigla)
{
return this.SelecionarPor(“Sigla”, sigla);
}
public IList<Uf> ListarPorDescricao(string descricao)
{
string hql = @”select uf from Uf uf
where uf.Descricao like :descricao
order by uf.Descricao”;
IQuery query = Get<ISession>().CreateQuery(hql);
query.SetAnsiString(“descricao”, descricao + “%”);
return query.List<Uf>();
}
public IList<Uf> ListarPorSigla(string sigla)
{
ICriteria crit = Get<ICriteria>()
.Add(Expression.InsensitiveLike(“Sigla”, sigla, MatchMode.Start))
.AddOrder(Order.Asc(“Descricao”));
return crit.List<Uf>();
}

Veja que criamos 03 métodos DAO´s. Um com critéria, outro com HQL (Hibernate Query Language) e outro utilizando um método nativo da classe BaseDAO. Veja que o controle da sessão do Hibernate vem do Get<ISession>() e o critéria vem do Get<ICriteria>(). Ambos implementados no BaseDAO. Veja também que com a utilização da nossa bilbioteca a construção de métodos de acesso a dados com Hibernate é bem facilitada. Abra tambem a interface IUfDAO.cs e adicione as seguintes linhas:

Uf SelecionarPorSigla(string sigla);
IList<Uf> ListarPorDescricao(string descricao);
IList<Uf> ListarPorSigla(string sigla);

Qualquer novo método que for adicionado nos DAO´s deverá também adicionar a declaração na interface correspondente. Agora abra o UfBO.cs e adicione as seguintes linhas:

public Uf SelecionarPorSigla(string sigla)
{
if (sigla == string.Empty || sigla == null)
throw new ExceptionRS(“Necessário informa a sigla”);
return ufDAO.SelecionarPorSigla(sigla);
}
public IList<Uf> ListarPorDescricao(string descricao)
{
if (descricao == string.Empty || descricao == null)
throw new ExceptionRS(“Necessário informa a descricao”);
return ufDAO.ListarPorDescricao(descricao);
}

Na regra de negócio nos fazemos chamadas aos nóvos métodos do DAO. Adicionamos uma pequena regra que determina que os parêmetros de entrada não podem ser vazio ou nulos. Todas essas validações podem ser colocadas aqui. Eu diria que DEVEM. Mas há quem coloquem apenas na página web através dos componentes ASP.NET de validações. Eu costumo validar os dados de entrada tanto na tela com os componentes de validação, quanto no objeto de négocio. Isso por que caso eu esqueça de validar na tela de entrada certamente no objeto de negócio uma excessão será gerada.

Como fizemos no IUfDAO.cs, vamos fazer com IUfBO.cs. Abra-o e adicione as seguintes linhas:

Uf SelecionarPorSigla(string sigla);
IList<Uf> ListarPorDescricao(string descricao);
IList<Uf> ListarPorSigla(string sigla);

Note que algumas linhas já estão lá definidas assim como no UfBO.cs e IUfBO.cs. Essas linhas foram adicionadas pelo RSClass.

Se desejarmos utilizar esses novos métodos na camada de apresentação precisamos primeiro declará-los na camada de negócio. Isso para obedecer a hirerarquia entre as camadas. Vamos faze isso. Abra o UfBO.cs e adicione os seguintes métodos:

public Uf SelecionarPorSigla(string sigla)
{
return ufDAO.SelecionarPorSigla(sigla);
}
public IList<Uf> ListarPorDescricao(string descricao)
{
return ufDAO.ListarPorDescricao(descricao);
}
public IList<Uf> ListarPorSigla(string sigla)
{
return ufDAO.ListarPorSigla(sigla);
}

Também será necessário acrescentar algumas linhas na interface IUfBO.cs:

Uf SelecionarPorSigla(string sigla);
IList<Uf> ListarPorDescricao(string descricao);
IList<Uf> ListarPorSigla(string sigla);

Lá já tem várias métodos declarados. São os métodos que podem ser chamados na camada de apresentação. Agora nós adicionamos mais 03 métodos para serem utilizados. Após estes procedimentos, os novos métodos já estão disponíveis para utilização na página. Fica como exercício: modificar a página para localizar dados por descrição. Dica: O RSClass possui outros layouts de página para serem gerados. Cada layout tem uma finalidade bem definida. Para mais informações acesse http://rsclass.wordpress.com.

É possível modificar os arguivos gerados para que os DAO´s, BO´s e banco de dados possam rodar em um servidor de aplicação, enquanto que o servidor web e/ou outros programas desktop rodam em outras máquinas fazendo solicitações ao servidor. Mas isso é assunto de posts futuro.

Deixo como exercício para o leitor implementar as outras páginas e recursos do site de acordo com a especificação inicial. Também me coloco a disposição para tirar dúvidas, dar treinamento, sugestões,  etc. Veja a página de Contato e sabia como me localizar. Acesse http://tutorialagendatelefonica.googlecode.com para baixar o código-fonte completo deste tutorial.

CONCLUSÃO

Desenvolver sistema é um trabalho desgastante. Mas este desgaste pode ser bem diminuido. Padrões de projetos, geradores de código, pesquisas e toda a engenharia de software podem ser aliados nessa tarefa de desenvolver bons programas.

Aproveito para indicar nossa área de Sistemas c/ Fontes. Adiquira um projeto completo para auxiliar no seu estudo e contribuir em nossas pesquisas.

Desde já agradeço a todos e até os próximos.


Categorias

Arquivos

Calendário

Agosto 2008
D S T Q Q S S
« Jul   Jan »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

Desde (04/11/07)

  • 44,809 visitas