Tag Archives: Web

Model Binding de enumeration no Struts2

Depois de perder 1 dia inteiro pesquisando e de inúmeras tentativas falhas de como usar o model binding do struts2 com enumerations do Java5, eu finalmente encontrei a solução em um site Japonês. Sim, Japonês. Não entendi nada do que estavam falando, mas consegui entender parte do código, o que foi suficiente.

Resolvi registrar para ajudar quem estiver passando pelo mesmo problema. Infelizmente não tenho o link da onde tirei a solução, se eu achar novamente, eu coloca aqui a fonte.

Usando a seguinte enum e entidade:

public enum Sexo {
    Masculino, Feminino
}

public class Cliente {
    private Sexo sexo;
    public void setSexo(Sexo sexo) {
        this.sexo = sexo;
    }
    public void setSexo(String sexo) {
        this.sexo = Sexo.valueOf(sexo);
    }
    public Sexo getSexo() {
        return this.sexo;
    }
}

A view ficaria assim:

<select name="cliente.sexo">
	<option <s:if test="%{cliente.sexo == @com.pacote.Sexo@Masculino}">selected="selected"</s:if> value="Masculino">Masculino</option>
	<option <s:if test="%{cliente.sexo == @com.pacote.Sexo@Feminino}">selected="selected"</s:if> value="Feminino">Feminino</option>
</select>

Neste caso, estou considerando que a action tenha uma propriedade do tipo Cliente com nome cliente.
Eu havia tentado de tudo ali nos atributos values da tag option, principalmente 0 e 1 por estar acostumado com o ASP.NET MVC. Mas acabei descobrindo que nas values o correto é ir o nome dos valores da enumeração e a entidade precisa ter um método set que receba uma string. Veja na classe Cliente o método setSexo(String), dentro dele, apenas converto para a enumeração.

Até a próxima!

Configurar NHibernate em um projeto Web

Neste tutorial vou explicar como dar o primeiro passo com o NHibernate. A configuração não é nada trivial e por isso gera bastante dúvidas.

Vou estar usando a versão mais recente do NHibernate até o momento, se você quiser usar a mesma versão, utilize este link para fazer o download.

Primero passo, criando a estrutura de projetos

Abra o Visual Studio, crie um projeto Web Application e uma Class Library na mesma solução. Vai ficar assim:

Obs.: Eu tenho o costume de deletar a pasta App_Data e a classe Class1 porque não vou utilizar.

Segundo passo, criando o arquivo de configuração

Dentro do projeto Class Library, crie um xml e chame-o de hibernate.cfg.xml. Atenção, é hibernate e não nhibernate!

Dentro deste arquivo ficam as configurações do banco de dados que iremos usar. Aqui será informado qual o provider (sqlserver, mysql, oracle etc), qual a string de conexão, dialeto etc.

Estou usando MSSQL Server e meu arquivo de configuração ficou assim:


<?xml version="1.0" encoding="utf-8"?>
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
 <session-factory name="NHibernate">
 <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
 <property name="connection.connection_string">Server=(local);initial catalog=MinhaBase;Integrated Security=SSPI</property>
 <property name="show_sql">false</property>
 <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
 <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
 <property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
 <mapping assembly="MercadoVader.NHibernateModule" />
 </session-factory>
</hibernate-configuration>

Obs.: A única configuração que muda entre meu arquivo e o seu será a string de conexão. No meu caso estou usando localhost com um banco de dados chamado e usando windows authentication.

Clique com o botão direito no arquivo hibernate.cfg.xml e vá em suas propriedades, na opção Copy to Output Directory escolha o valor Copy Always.

Terceiro passo, banco de dados

Não há muito segredo aqui, apenas crie uma base de dados e rode este script para criar uma tabela de produtos.


CREATE TABLE [dbo].[PRODUTO](
 [ID] [int] IDENTITY(1,1) NOT NULL,
 [NOME] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 [PRECO] [decimal](18, 2) NOT NULL,
 [DATA_CADASTRO] [datetime] NOT NULL,
 CONSTRAINT [PK_PRODUTO] PRIMARY KEY CLUSTERED
(
 [ID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

Terceiro passo, banco de dados

Não há muito segredo aqui, apenas crie uma base de dados e rode este script para criar uma tabela de produtos.


CREATE TABLE [dbo].[PRODUTO](
 [ID] [int] IDENTITY(1,1) NOT NULL,
 [NOME] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 [PRECO] [decimal](18, 2) NOT NULL,
 [DATA_CADASTRO] [datetime] NOT NULL,
 CONSTRAINT [PK_PRODUTO] PRIMARY KEY CLUSTERED
(
 [ID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

Quarto passo, criando um HttpModule pro NHibernate

Essa parte é mais chata, primeiro de tudo, selecione o projeto Class Library e adicione referência à NHibernate.dll que está na pasta Required_Bin dentro do arquivo zip que foi baixado no início do tutorial. Adicione também uma referência à NHibernate.ByteCode.Castle que também veio no arquivo zip. Por último, adicione referência à System.Web, será necessário para poder criar uma classe que herde da System.Web.IHttpModule.

Crie uma classe chamada NHibernateSessionModule dentro da Class Library e coloque este conteúdo nela:

public class NHibernateSessionModule : IHttpModule
{
    public static readonly string KEY = "NHibernateSession";
    private static ISession _session;

    public void Dispose()
    {
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.EndRequest += new EventHandler(context_EndRequest);
    }

    private void context_EndRequest(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication)sender;
        HttpContext context = application.Context;

        ISession session = context.Items[KEY] as ISession;
        if (session != null)
        {
            try
            {
                session.Flush();
                session.Close();
            }
            catch { }
        }
        context.Items[KEY] = null;
    }

    private static ISessionFactory factory = null;

    private static ISessionFactory GetFactory()
    {
        if (factory == null)
        {
            Configuration config = new Configuration();
            if (config == null)
                throw new InvalidOperationException("NHibernate configuration is null.");

            config.Configure();

            factory = config.BuildSessionFactory();
            if (factory == null)
                throw new InvalidOperationException("Call to Configuration.BuildSessionFactory() returned null.");
        }
        return factory;

    }

    public static ISession OpenSession()
    {
        ISession session;
        session = GetFactory().OpenSession();
        if (session == null)
            throw new InvalidOperationException("Call to factory.OpenSession() returned null.");
        return session;
    }

    public static ISession CurrentSession
    {
        get
        {
            if (HttpContext.Current == null)
            {
                if (_session != null)
                {
                    return _session;
                }
                else
                {
                    _session = OpenSession();
                    return _session;
                }
            }
            else
            {
                HttpContext currentContext = HttpContext.Current;
                ISession session = currentContext.Items[KEY] as ISession;
                if (session == null)
                {
                    session = OpenSession();
                    currentContext.Items[KEY] = session;
                }
                return session;
            }
        }
    }

    private void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication)sender;
        HttpContext context = application.Context;
        context.Items[KEY] = OpenSession();
    }
}

Pelo fato de esta classe herdar de IHttpModule, ela será chamada toda vez que um HttpRequest for feito para sua aplicação Web. Esta classe cria uma objeto do tipo Session para cada request e só destroi ele no final.Isso garante que teremos apenas uma Sessão aberta para cada request.

Essa parte não é necessária fazer, mas é considerado uma boa prática trabalhar com apenas uma Sessão ao invés de sair criando várias.

Abra o arquivo web.config e procure pela tag httpModules, dentro desta tag adicione mais um valor, que será referência ao módulo que acabamos de criar. Fica assim:

<httpModules>
 <add name="NHibernateSessionModule" type="MercadoVader.NHibernateModule.NHibernateSessionModule, MercadoVader.NHibernateModule"/>
 <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>

Quinto passo, o mapeamento

Ufa, quase acabando :)

Dentro da Class Library crie uma classe chamada Produto. Crie também uma pasta chamada Mapping e dentro dela crie o arquivo Produto.hbm.xml.

A classe produto fica assim:

public class Produto
{
    public int Id { get; set; }
    public string Nome { get; set; }
    public DateTime DataCadastro { get; set; }
    public decimal Preco { get; set; }
}

O arquivo Produto.hbm.xml fica assim:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="MercadoVader.NHibernateModule" assembly="MercadoVader.NHibernateModule">

  <class name="Produto" table="PRODUTO">

    <id name="Id">
      <column name="ID" sql-type="int" not-null="true"/>
      <generator class="identity" />
    </id>

    <property name="Nome" type="String">
      <column name="NOME" length="100" not-null="true" />
    </property>

    <property name="Preco" type="Decimal">
      <column name="PRECO" precision="18" scale="2" not-null="true" />
    </property>

    <property name="DataCadastro" type="DateTime">
      <column name="DATA_CADASTRO" not-null="true" />
    </property>
  </class>

</hibernate-mapping>

O que estamos fazendo aqui é criar uma classe que é espelho da nossa tabela no banco de dados e também criando um arquivo de mapeamento que liga a classe com a tabela, nada muito complexo.

Clique com o botão direito no arquivo Produto.hbm.xml e vá em suas propriedades. Na opção Build Action selecione o valor “Embedded Resource”, isso fará com que o arquivo seja compilado dentro de uma DLL.

Sexto passo, será que funciona funciona?

Para testar, você deve selecionar o projeto Web e adicionar referência ao projeto Class Library e também à NHibernate.dll.

Feito isso, entre no Default.aspx.cs e coloque isso no Page_Load:

protected void Page_Load(object sender, EventArgs e)
{
    Produto produto = new Produto();
    produto.Nome = "Biscoito Trakinas";
    produto.Preco = 1.10M;
    produto.DataCadastro = DateTime.Now;
    NHibernateSessionModule.CurrentSession.Save(produto);
}

Se tudo der certo, deverá haver um registro salvo no banco de dados agora.
Deu certo? Parabéns, o NHibernate está configurando e funcionando.
Não deu? Faça um comentário descrevendo o erro e verei se consigo ajudar.

Documentação do NHibernate está aqui: https://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/

Utilize a documentação para aprender sobre HQL e como buscar dados do banco de dados.
Conforme eu havia comentado neste post, o site summer of nhibernate é muito bom para aprender.
Se eu escrevi algo errado, por favor, me corrijam, já é meio tarde e o sono já está batendo.

Aqui está o projeto que eu criei para download.

Clique aqui para fazer o download de 'MercadorVader, NH + ASP.NET' (1.09 MB)

Até a próxima!

Buscando o preço de e-commerces brasileiros usando PHP

Infelizmente nenhum e-commerce brasileiro possui uma API para integração. Imagine as possibilidades de mashup que poderiam ser criados se o Submarino e/ou Americanas liberassem uma API para consulta de produtos e de preços.

Porém existem outras formas de fazer isso, possuem N desvantagens quando comparada com uma API, mas funciona. A maneira mais fácil é fazer um httpRequest para a site do e-commerce e buscar o preço dentro do HTML que vem no httpResponse.

Quer ver um exemplo em PHP? Estou fazendo o uso de uma biblioteca FANTÁSTICA escrita em PHP, ela se chama simplehtmldom e é usada para fazer o parser de um HTML. Veja como ficou:


<?php
include('simple_html_dom.php');

//URL do produto
$url = "http://www.submarino.com.br/produto/6/21619508";

//Nome da loja
//Valores possíveis: Americanas, Submarino
$loja = "Submarino";

$html = file_get_html($url);
if ($loja == "Americanas") {
 $pattern = "dd.acomPrice";
} else if ($loja == "Submarino") {
 $pattern = "strong.for span";
}

foreach($html->find($pattern) as $element) {
 echo $element->innertext . "<br/>";
}
?>

Funciona de uma forma bem simples, a função file_get_html faz um request para o endereço que está na variável $url, método find você passa uma expressão para buscar algum conteúdo dentro do resultado HTML que da $url. Para quem conhece jQuery vai perceber que a sintaxe do “pattern” é o mesmo, extremamente simples e fácil de usar.

No caso do Americanas, estamos procurando todos os elementos dd que possuem a classe css acomPrice, dentro desta tag é que está o preço. No submarino o preço está dentro de um span que, por sua vez, está dentro de um strong com a classe css for.

Se você precisa buscar o preço de um produto em alguma loja que não seja essas duas, basta aninhar mais um if ali na verificação da loja e jogar na variável $pattern o caminho do preço no e-commerce que você precisa.

Se você está pensando em fazer uma busca de preço em massa, ou seja, buscar o preço de 100 ou mais  produtos, eu recomendo que você faça uma pausa de 5~10 segundos para não sobrecarregar o servidor do e-commerce. Use com moderação.

Clique aqui para fazer o download de 'Buscador preço PHP' (7.44 kB)

Este é o arquivo de exemplo que coloquei no post e também a biblioteca simplehtmldom.

UPDATE 30/04/2011: Como este script se baseia na estrutura da página HTML dos e-commerces, ele pode parar de funcionar a qualquer momento caso alguma alteração na página do e-commerce seja feita. É necessário alterar este script para atender a nova estrutura.

Sharing Buttons by Linksku