segunda-feira, novembro 19, 2007

Framework Mentawai - Desenvolvimento Java para WEB.

Dae pessoal, to a tempos para postar aqui mas não consigo tempo para nada.

Estes dias ia iniciar um projeto e comecei pela escolha de um framework para utilizar. O pessoal da empresa em que eu trabalhava (a dois meses mudei de emprego, estado, cidade... e por isso de ter largado o blog um pouco) estava usando o Jboss SEAM para novas aplicações WEB. Eu gostei, fui em uma palestra sobre o Jboss Seam e tudo mais, mas no fim achei que ia usar uma bomba atômica para matar uma formiga. E depois, queria aprender algo realmente novo e que ninguém naquela sala tivesse trabalhado, por desafio mesmo. Então pensei em dar uma olhada nos frameworks brasileiros (não, não li a revista aquela com a matéria de capa sobre isso). De cara na home do site do primeiro candidato eu já vi o que mais me agradaria: sem XMLs e annotations!

Claro, estou falando do Mentawai. Projeto bem bacana que anda conquistando o mundo (ta isso foi por minha conta :) )!!!

Então como de costume, peguei o .jar do framework, criei um novo projeto no Eclipse e comecei a fuçar criando minha aplicação modelo, se eu conseguisse fazer ali meia dúzia de coisas que eu desejava na minha aplicação final ele seria o eleito.

Comecei pela lista:

  1. Não quero preencher beans na mão, o framework tem que pegar os formulários e popular objetos para mim.

  2. Tem que ter algum suporte a IoC sem que eu tenha que meter mais uma tonelada de .jars, como o do Spring e a penca de .jars que ele necessita, para isso e sem muito esforço também, não quero ter que ler mais uma documentação para aprender como usar um framework secundário.

  3. Bom, pela minha preguiça expressa acima eu também não quero ter que correr atrás de componentes como upload, envio de e-mail, etc... o eleito tinha que ter tudo isso.

  4. O Mentawai de cara já era um futuro eleito por não conter um só maldito XML e nem uma porcaria de annotation! Não, sério... na boa... aquela sopa de annotations ou aqueles duzentos arquivos XML não é prático.


Meu exemplo era o mais simples: uma tela de acesso ou cadastro. Ok, vamos complicar um pouco, mas nem tanto, tinha que ter suporte a internacionalização e salvar algumas coisas sobre o usuário em cookies.

Em 4 minutos (na verdade 3 e alguns segundos) eu tinha feito a primeira tela da minha aplicação seguindo a documentação no site do framework.

Toda a configuração da aplicação é feita em uma classe, ou seja, é em Java! Não tem como o cara colocar um caractere inválido, esquecer de fechar uma tag ou fazer qualquer barbeiragem neste arquivo pois o mesmo é compilado. Ótimo, gostei muito disso.

Depois da minha primeira página parti para a forma de pegar os dados do formulário de cadastro em um objeto já preenchido pelo framework. Moleza. Isso foi muito fácil de fazer também.

Login... tenho que criar a forma do cara entrar na minha aplicação e recuperar os dados dele da sessão. Também não foi difícil. Tive uns problemas aqui mas foram barbeiragens minhas :) o frame estava certo!

Comecei a achar que estava no mundo perfeito!

Passei para a internacionalização. Aqui eu tive que apelar para o fórum. Mas fui socorrido em minutos.

A única coisa que ainda falta eu implementar é usar mensagens parametrizadas na internacionalização. Exemplo: O usuário {0} deletou {1}. Mas, diz o inventor do monstro, que há como fazer e eu que não li a documentação corretamente. Devo tirar um tempinho essa semana para descobrir como fazer.

Então minha aplicação ficou assim:

Passos da aplicação:

  1. Ao acessar o endereço da aplicação no navegador o usuário é redirecionado para a action Preparesystem. Esta action tem a função de ler possíveis dados do usuário armazenados anteriormente, como o último login que acessou o sistema, idioma escolhido anteriormente pelo usuário, etc... Depois de feito isso o usuário é redirecionado para a página principal do sistema.

  2. Na página principal o usuário tem duas opções: fazer login ou cadastrar-se no sistema.

  3. Quando o usuário executa login ele é redirecionado para a action Login. Nesta action é injetado o objeto User pelo controle de IoC do Mentawai. Ou seja, é pego na requisição os dados do formulário e postos em um objeto sem a intervenção do desenvolvedor (magavilha!). Depois de aplicada a regra de acesso ao sistema o usuário é redirecionado para a home, caso o login tenha sido bem sucedido, ou para a primeira página do sistema novamente onde serão exibidas as mensagens de erro, caso o login não tenha sido bem sucedido. Note que quem faz a validação da requisição é uma classe externa a action, chamada LoginValidator. Talvez essa seja a grande jogada deste framework, poder separar muito bem as coisas em objetos com propósitos distintos.

  4. Quando o usuário vai se cadastrar no sistema ele é redirecionado para a página com o formulário de cadastro. Por trás desta página há a action Register que, assim como a action Login, recebe um objeto já preenchido com os dados do formulário. Também a exemplo da action Login, a validação está em uma classe externa chamada RegisterValidator. Se o cadastro for efetivado com sucesso o usuário é redirecionado para uma página de boas vindas de onde ele tem a opção de ir para a home do sistema.


E estamos aí, ela funciona mesmo! Olha a cara dela que espetáculo! :)


Tela inicial da aplicação

Assim começa a aplicação.



Tela inicial da aplicação em inglês

Versão em inglês.



Tela cadastro

Tela de cadastro.



Tela de boas-vindas

Boas-vindas para usuários que se cadastram.



Tela inicial para usuários logados

Home para usuários logados.


Quem quiser baixar os fontes e olhar como foi feito: http://www.mentaframework.org/files/. Enviei para o criador do framework minha app para que ela fique disponível como exemplo de uso.

Era isso pessoal. Encorajo-os a usar o framework, cadastrassem no fórum, e darem algum valor pelo o que é desenvolvido com tanto esforço pelos programadores tupiniquins!

Abraços a todos.

OBS.: Não postem comentários sobre perguntas referentes ao framework aqui, e sim no fórum deles! Postem comentários sobre a minha aplicação, erros de português, falando do blog... enfim.

quarta-feira, setembro 19, 2007

Wiki sobre MySQL em português!

Dae galera, quem visita o blog com frequência sabe que eu me dedico aqui a postar coisas sobre Java e sobre MySQL.


Sobre Java porque é o que eu faço profissionalmente e sobre MySQL porque é o SGBD que mais uso nos meus projetos particulares.


Depois de anos participando da lista MySQL-Br do Yahoo! Brasil o pessoal criou uma página de Wiki para facilitar buscas por conteúdos e artigos.


Bem, o site do Wiki começou hoje, e não tem nada ainda. Mas vale a pena ficar de olho e de vez em quando dar uma passadinha por lá.


Os endereços:


Da lista no Yahoo! Brasil: http://br.groups.yahoo.com/group/mysql-br/


Do Wiki: http://mysql-br.wikidot.com.


Colaborações serão sempre bem-vindas!

quarta-feira, setembro 05, 2007

Internacionalização em Java

Dae pessoal, não faz tanto tempo assim que postei algum “drop” de sabedoria aqui. Então vamos a mais um: internacionalização!

Diretamente das definições da Sun: “internacionalização é o processo de projetar uma aplicação, de modo que ela possa ser adaptada a varias linguagens e regiões sem mudanças de engenharia.” – leia-se mudanças no código.

Bom, você já deve ter ouvido ou lido em algum código o prefixo “i18n” ou “l10n”. i18n é a abreviação de internationalization, pois do primeiro i ao último n há 18 letras. O l10n é a abreviação de localization e segue a mesma regra do número de caracteres entre l e n.

Existem certas características em um programa internacionalizado, como:

• Dado uma localização (Locale), o mesmo binário do programa deve funcionar em qualquer lugar;
• Os dados textuais do programa (mensagens de erro, mensagens de tela, rótulos, etc...) não devem estar atribuídos de forma “hardcode” aos elementos dentro dos códigos do programa e sim externados em um arquivo apropriado;
• Suporte a outros idiomas não deve requerer nova compilação do programa;
• Dados dependentes de localização, como formatação de datas e campos monetários, devem ser apresentados de acordo com a região do usuário e seu idioma;

Bom, se você tiver sons e imagens que possam fazer sentido internacionalizar, é uma boa idéia!

Vamos cortar o nhe nhe nhe e partir para a ação.

Vamos a um simples exemplo. Uma classe que deva imprimir:

Nome: Jean Jorge Michel
Nome de usuário: jmichel
Senha: minhasenhaultrasecreta

Porém os rótulos (nome, nome de usuário e senha) devem ser impressos corretamente para cada idioma (tá, não exageremos, vamos testar com português e inglês apenas).

Então vamos começar pelos fatos primeiros (como diria o grande sábio), criando os arquivos com a tradução do nosso programa. Para isso, no pacote estudo.internacionalizacao criei dois arquivos:

• ResourceBundle_pt_BR.properties;
• ResourceBundle_en_US.properties.

E o conteúdo de cada um deles (na ordem de criação):

name=Nome
password=Senha
username=Nome de usuário

name=Name
password=Password
username=Username

Agora no mesmo pacote criei a classe Internacionalizada.java:

package estudo.internacionalizacao;

import java.util.Locale;
import java.util.ResourceBundle;

public class Internacionalizada {

    public static void main(String[] args) {

        final Locale defaultLocale = Locale.getDefault();
        final ResourceBundle rbDefault = ResourceBundle.getBundle("estudo.internacionalizacao.ResourceBundle", defaultLocale);

        System.out.println(new StringBuilder().append(rbDefault.getString("name")).append(": Jean Jorge Michel"));
        System.out.println(new StringBuilder().append(rbDefault.getString("username")).append(": jmichel"));
        System.out.println(new StringBuilder().append(rbDefault.getString("password")).append(": minhasenhaultrasecreta").append("\n\n"));

        final Locale otherLocale = new Locale("en", "GB");
        final ResourceBundle rb = ResourceBundle.getBundle("estudo.internacionalizacao.ResourceBundle", otherLocale);

        System.out.println(new StringBuilder().append(rb.getString("name")).append(": Jean Jorge Michel"));
        System.out.println(new StringBuilder().append(rb.getString("username")).append(": jmichel"));
        System.out.println(new StringBuilder().append(rb.getString("password")).append(": minhasenhaultrasecreta"));
    }
}

Agora é só rodar e ver o resultado das duas saídas que devem parecer com isso:

Nome: Jean Jorge Michel
Nome de usuário: jmichel
Senha: minhasenhaultrasecreta


Name: Jean Jorge Michel
Username: jmichel
Password: minhasenhaultrasecreta

Lembra daquela Enum do post anterior? Poderia ser internacionalizada não? :)

Até mais! Bons códigos

quinta-feira, agosto 16, 2007

Java Enum

Aeeee o blog está vivo! Eu andei sem tempo para postar aqui mas vamos lá...

A bola da vez é o tipo Enum do Java.
Uma enum (enumeração) é um conjunto de valores fixos (constantes) que podem ajudar o desenvolvedor a definir valores fixos que serão usados no sistema.

Use sempre que você quiser definir um conjunto de valores fixo como por exemplo os dias da semana, meses do ano, tipos de entidades, etc...

Vamos ao exemplo usando uma enum que armazene os dias da semana:

public enum DiasSemana {

    SEGUNDA, TERCA, QUARTA, QUINTA, SEXTA, SABADO, DOMINGO;

}

Ok, o exemplo é bem meia boca, mas vamos em frente. Por serem constantes os valores de uma enum são sempre grafados em maiúsculo.

Agora vamos fazer uma classe que utilize esses valores:

public class Agenda {
    public static void main(String[] args) {
    
        System.out.println(DiasSemana.QUINTA);
    
    }
}

A saída é: QUINTA.

Enums podem ter métodos, seus valores podem ter métodos, e toda enum “herda” métodos (nos materiais da SUN eles falam que o compilador adiciona tais métodos, não fala em herança propriamente dita e na documentação da classe o objeto Enum é filho de Object. Todas as enums que você criar serão filhas da classe java.lang.Enum e por não haver herança múltipla no Java você não pode fazer suas enums estenderem mais nada!) que facilitam a vida do desenvolvedor como o estático values() que retorna um array com os valores da enum ordenados pela declaração. Ex:

public class Agenda {
    public static void main(String[] args) {

        for(DiasSemana dia : DiasSemana.values()) {
            System.out.println(dia);
        }

    }
}

A saída é:

SEGUNDA
TERCA
QUARTA
QUINTA
SEXTA
SABADO
DOMINGO

Bem mas a nossa enum não está muito funcional, eu não exibiria em meu sistema essas entradas! ;)
Então vamos declarar métodos que formatem nossos valores para algo melhor:

public enum DiasSemana {

    SEGUNDA() {
        public String extenso() {
            return "segunda-feira";
        }
    },
    TERCA() {
        public String extenso() {
            return "terça-feira";
        }
    },
    QUARTA() {
        public String extenso() {
            return "quarta-feira";
        }
    },
    QUINTA() {
        public String extenso() {
            return "quinta-feira";
        }
    },
    SEXTA() {
        public String extenso() {
            return "sexta-feira";
        }
    },
    SABADO() {
        public String extenso() {
            return "sábado";
        }
    },
    DOMINGO() {
        public String extenso() {
            return "domingo";
        }
    };
    
    public String extenso() {
        return this.extenso();
    }
}

Vamos usar o recurso:

public class Agenda {
    public static void main(String[] args) {

        for(DiasSemana dia : DiasSemana.values()) {
            System.out.println(dia.extenso());
        }

    }
}

A saída é:

segunda-feira
terça-feira
quarta-feira
quinta-feira
sexta-feira
sábado
domingo

Beleza! Com isso foi ilustrado o fato de um tipo Enum poder ter métodos e seus atributos também.

Ainda brincando com a classe Agenda:

public class Agenda {
    public static void main(String[] args) {

        for(DiasSemana dia : DiasSemana.values()) {
            System.out.println("Valor: " + dia);
            System.out.println("Extenso: " + dia.extenso() + "\n");
        }

    }
}

A saída é:

Valor: SEGUNDA
Extenso: segunda-feira

Valor: TERCA
Extenso: terça-feira

Valor: QUARTA
Extenso: quarta-feira

Valor: QUINTA
Extenso: quinta-feira

Valor: SEXTA
Extenso: sexta-feira

Valor: SABADO
Extenso: sábado

Valor: DOMINGO
Extenso: domingo

Bom galera, foi uma passadinha rápida por aqui para postar algo. Até mais, obrigado pela visita, click nos patrocinadores :) e comentários.

quinta-feira, julho 19, 2007

Mais de um mês parado mas ...

Dae pessoal, sei que o blog anda meio largado mas é por um bom motivo: estudando JBoss SEAN!
Parece ser muito bacana o negocio, e espero que resolva o meu problema de preguiça aguda na hora de implementar algumas coisas :P

Tirando isso to levando um coro para configurar um ambiente de trabalho no Windows Vista Home Premium assim como configurá-lo e conseguir navegar na internet sem me arrastar... mas enfim.

Esperam por um post sobre JBoss SEAN, JSF e afins.

A nova, nem tão nova, é que estou usando o Eclipse Europa agora... desisti do Lomboz.

Até a próxima pessoal.

quarta-feira, junho 13, 2007

Prepared Statements no MySQL.

Esses tempos tinha uma pergunta pipocando em uma lista sobre MySQL onde uma pessoa queria fazer alguma coisa em um procedimento (procedure) e não conseguia. A solução que eu encontrei para ajudar a pessoa foi Prepared Statements.

Para ilustrar o seu uso criei uma tabela:

CREATE TABLE PEDIDO (
  CODPEDIDO  INT(3) ZEROFILL NOT NULL,
  CODCLIENTE INT(3) ZEROFILL NOT NULL,
  VALOR      DOUBLE          NOT NULL,
  STATUS     CHAR(1)         NOT NULL
);

Depois inseri dados:

INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(1, 1, 99.87, 'E');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(2, 2, 99.87, 'A');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(3, 3, 99.87, 'A');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(4, 2, 99.87, 'S');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(5, 2, 99.87, 'A');
INSERT INTO Pedido(CodPedido, CodCliente, Valor, Status) VALUES(6, 3, 99.87, 'E');

O Status seria: E – Encerrado, A – Aberto, S – Suspenso.

Depois pode se criar o Prepared Statements, eu fiz dentro de um procedimento, mas poderia ser feito direto no console do MySQL que funcionaria.

Criando o procedimento (procedure):

CREATE PROCEDURE P_PEDIDOS_STATUS(IN STATUS CHAR(1))
BEGIN

  SET @StatusPedido = STATUS;

  PREPARE psmtp FROM 'SELECT CodPedido, CodCliente, Valor, Status FROM Pedido WHERE Status = (?) GROUP BY CodCliente ORDER BY CodPedido';
  EXECUTE psmtp USING @StatusPedido;

  DEALLOCATE PREPARE psmtp;

END;

Bom a finalidade aqui é selecionar todos os pedidos por status, para isso passamos por parâmetro o status que desejamos procurar:

Resultados:

mysql> call P_PEDIDOS_STATUS('E');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       001 |        001 | 99.87 | E      |
|       006 |        003 | 99.87 | E      |
+-----------+------------+-------+--------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

mysql> call P_PEDIDOS_STATUS('A');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       002 |        002 | 99.87 | A      |
|       003 |        003 | 99.87 | A      |
+-----------+------------+-------+--------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

mysql> call P_PEDIDOS_STATUS('S');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       004 |        002 | 99.87 | S      |
+-----------+------------+-------+--------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

Agora, e se quisermos ter um procedimento maluco que receba toda a minha query e a execute? Sei lá qual a finalidade disto (agora não me ocorre nada em mente que justifique, mas vai que você tem um relatório que muda muito pouco de uma query – por exemplo só o where ou o order by -  e pode generalizar a chamada passando a query ou parte dela? Sei lá!) então poderiamos “injetar” para o procedimento todo o comando a ser executado no SGBD:

CREATE PROCEDURE P_INJETA_SQL(IN SQLCOMMAND VARCHAR(500))
BEGIN

  SET @Command = SQLCOMMAND;

  PREPARE psmtp FROM @Command;
  EXECUTE psmtp;

  DEALLOCATE PREPARE psmtp;

END;

Teste:

mysql> CALL P_INJETA_SQL('SELECT CodPedido, CodCliente, Valor, Status FROM Pedido WHERE Status = ''E'' GROUP BY CodCliente ORDER BY CodPedido');
+-----------+------------+-------+--------+
| CodPedido | CodCliente | Valor | Status |
+-----------+------------+-------+--------+
|       001 |        001 | 99.87 | E      |
|       006 |        003 | 99.87 | E      |
+-----------+------------+-------+--------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

Beleza, afunfa!

Bom de asas a sua imaginação agora e qualquer coisa comenta aí! :D

sexta-feira, junho 01, 2007

Palestra sobre Hibernate e Jboss SEAM

Ontem fui a duas palestras na Targettrust, uma delas sobre Hibernate e a outra sobre o Jboss Seam.

Sai de lá me perguntando: porque eu fui? :) Com um monte de coisas que eu preciso estudar para finalmente marcar minha prova de certificação agora fiquei ansiando por aprender mais sobre o SEAM.

Muito boa a palestra, pena que teve seu tempo drasticamente encurtado mas mesmo assim muito legal.

O material está no blog do autor: http://www.juliocsmac.blogspot.com.

Bom, mais além eu irei escrever sobre o SEAM e tal...

Por hora é isso, a vida ta corrida! :P

quarta-feira, maio 23, 2007

Modificador Static

Você sabe o que significa a bendita palavra static no Java?
Vejo muita gente fazendo confusão sobre esse assunto, sem entender o que é ou quando usar etc...
Bem, como de costume, lá vem um exemplinho básico do emprego de variáveis estáticas.

Imagine que nós estamos aqui, sem nada para fazer, e vamos representar dois objetos do mundo real em condições de uso típicas do mundo real (é, nada daqueles exemplos esdrúxulos como classe xyz variáveis foo, bar... :P): duas bicicletas.

Uma delas é uma montainbike, e o camarada está se desprendendo do topo de uma montanha qualquer no melhor estilo suicida, o outro cara está em uma bicicleta do tipo de corrida de velocidade, subindo uma ladeira.

Bom vamos ver o que as nossas classes precisam ter para que se represente os objetos reais:

- Tipo;
- Modelo;
- Número de marchas;
- Aro
- Marcha atual;
- Velocidade.

Bem, vamos então a nossa classe Bibicleta:

public class Bicicleta {

    private String tipo;
    private String modelo;
    private int marchas;
    private int aro;
    private int marchaCorrente;
    private static double velocidade;

    public String getTipo() {
        return tipo;
    }

    public void setTipo(String tipo) {
        this.tipo = tipo;
    }

    public int getAro() {
        return aro;
    }

    public void setAro(int aro) {
        this.aro = aro;
    }

    public int getMarchas() {
        return marchas;
    }

    public void setMarchas(int marchas) {
        this.marchas = marchas;
    }

    public String getModelo() {
        return modelo;
    }

    public void setModelo(String modelo) {
        this.modelo = modelo;
    }

    public void setMarchaCorrente(int marchaCorrente) {
        this.marchaCorrente = marchaCorrente;
    }

    public int getMarchaCorrente() {
        return this.marchaCorrente;
    }

    public static void frear() {
        Bicicleta.velocidade--;
    }

    public static void acelerar(int velocidade) {
        Bicicleta.velocidade += velocidade;
    }

    public static double getVelocidade() {
        return Bicicleta.velocidade;
    }

    public Bicicleta() {
    }

    public String toString() {
        String texto = "Tipo: " + this.getTipo() +
                       " Madelo: " + this.getModelo() +
                       " Marchas: " + this.getMarchas() +
                       " Aro: " + this.getAro();

        return texto;
    }

    public Bicicleta(String tipo, String modelo, int marchas, int aro) {
        this.tipo = tipo;
        this.modelo = modelo;
        this.marchas = marchas;
        this.aro = aro;
    }
}

Notem o seguinte, nossa classe tem um campo definido como estático (static), o campo velocidade. Estático na sua definição quer dizer: adj. relativo ao equilíbrio das forças, paralisado, imóvel, firme, em repouso. Paralisado é o mais importante para nós.

Imagine que um campo estático é um cara que uma vez que se cria o objeto e se atribui um valor a ele todas as demais estâncias da nossa classe passam a assumir este valor.

Ou seja criamos uma bicicleta com a nossa classe assim:

Bicicleta b0 = new Bicicleta("Montainbike", "Caloi Aluminum", 21, 26);

Agora vamos configurar alguns estados a ela:

b0.setMarchaCorrente(21);
b0.acelerar(45);

Beleza, o que quer dizer que o cara está na marcha 21 a 45km/h, porém vamos ver o que acontece ao criarmos mais uma bicicleta:

Bicicleta b1 = new Bicicleta("Speed", "Caloi Estrada", 10, 28);
b1.setMarchaCorrente(1);
b1.acelerar(9);

Bom, vejamos o que o método acelerar faz: Bicicleta.velocidade += velocidade; Bicicleta já indica que a coisa não é específica para o objeto que chamou o método (this. indicaria isso). Logo se tínhamos 45km/h atribuído ao primeiro objeto, agora adicionamos 9km/h e compartilhamos para ambos 54kh/h como valor para o campo , e pior os dois objetos estão errados agora. Cheque com o método getVelocidade assim: bo.getVelocidade() e b1.getVelocidade(). O resultado é 54!

Entendeu como os campos estáticos se comportam? Em um programa enquanto eu estiver estanciando Bicicleta todas elas estarão com o campo velocidade apontando para o mesmo valor em memória e sempre que alguma das estâncias modificar este valor todas as demais vão ter este campo alterado.

Simples! Agora adicione o método main abaixo listado na sua classe e execute-o para ver a saídas.

    public static void main(String[] args) {
        //Criando duas bikes de modelos e características diferentes
        Bicicleta b0 = new Bicicleta("Montainbike", "Caloi Aluminum", 21, 26);
        Bicicleta b1 = new Bicicleta("Speed", "Caloi Estrada", 10, 28);

        //Checando as duas após a criação
        System.out.println(b0.toString());
        System.out.println(b1.toString());

        //Configurando a primeira
        b0.setMarchaCorrente(21);
        b0.acelerar(45);

        System.out.println("b0 está a 45km/h"); //Até aqui ok

        //Configurando a segunda
        b1.setMarchaCorrente(1);
        b1.acelerar(9); //Será?

        System.out.println("b1 está a 9km/h"); //Errado! Está a 54!

        b0.frear(); //Errado novamente, ambas as bikes estão freando!

        /*
         * Constatamos que a velocidade dela esteve errada desde o momento da
         * criação da segunda bike, pois ao setarmos a velocidade da segunda
         * somamos a velocidade pretendida para a segunda bike com a que já
         * estava atribuída a primeira! Agora decrementando em 1km/h temos o
         * valor errôneo de 53km/h ao invés dos 44km/h pretendidos com a ação.
         */
        System.out.println("b0 está agora freando, velocida está em " +
                           b0.getVelocidade() + "km/h");

        System.out.println("Bicicleta b0 está na marcha " +
                           b0.getMarchaCorrente() + " a " + b0.velocidade +
                           " km/h");

        System.out.println("Bicicleta b1 está na marcha " +
                           b1.getMarchaCorrente() + " a " + b1.velocidade +
                           " km/h");
    }

Lembrando que métodos podem ser estáticos também. Onde normalmente se tem estância.método para poder invocar um método, métodos estáticos não precisam disto, podem (e devem) ser invocados sem a referência ao objeto invocador. Exemplo disso é em nossa própria classe Bicicleta, em que o método frear e acelerar como manipulam uma variável estática são declarados como estáticos, e estamos fazendo invocações a eles usando b0.acelelar() e b0.frear(). O correto deveria ser direto frear() e acelerar() já que afetará todas os objetos criados. Fica de tema de casa mudar as chamadas :P. Invocando só frear(), por exemplo, sem referência ao objeto fica até mais fácil de se “ver” que afetará todos os objetos.

T+ pessoal, gostou, não gostou, críticas, elogios, erros... estamos sempre aí, só mandar seu comentário (se possível deixa um e-mail para que eu possa lhes responder).


Aqui tem o post em pdf, para facilitar as coisas :P


http://geocities.yahoo.com.br/sadbuttruebr/blog/
posts_pdf/estaticos.pdf

sexta-feira, maio 18, 2007

Expressões Regulares - O início

Diretamente das definições da Sun :P :

Expressões regulares são uma maneira de qualificar um conjunto de strings baseado em características comuns compartilhadas por cada string no conjunto. Elas podem serem usadas para procurar, editar ou manipular texto e dados.

:(

Bem vou tentar desmistificar esse assunto fazendo, claro, um exemplo usando a API java.util.regex do Java.

Expressões regulares variam de complexabilidade mas uma vez que você aprender os conceitos básicos do assunto será capaz de lê-las e escrevê-las sem dificuldades.

Vamos primeiro ver 3 classes chaves para o uso de expressões regulares em Java: Pattern, Matcher e PatternSyntaxExeption.

a) Pattern: É a representação compilada da expressão regular. Não há um construtor público na classe. Para criar um pattern você precisa primeiro invocar um dos métodos compiladores públicos e estáticos da classe que retornarão um objeto Pattern. Estes métodos aceitam como argumento uma expressão regular.

b) Matcher: É o mecanismo de interpretação que interpretará o pattern e confrontará-o contra a string passada para ele. Assim como a classe Pattern, Matcher não define um construtor público. Você obtem um objeto do tipo Matcher invocando o método matcher do objeto Pattern.

c) PatternSyntaxException: Esta é facinha né! :) Nada mais é do que uma exceção que ocorre quando se tem um erro na sintaxe do pattern.

Vamos ao primeiro exemplo: uma classe que procure por ocorrências de um conjunto de caracteres (uma string) em uma sentença qualquer.

ExemploExpressoesRegulares.java

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExemploExpressoesRegulares {
public static void main(String[] args) {
        System.out.println("--- Teste Com Expressões Regulares ---");

        String sentenca = "Post sobre Expressões Regulares (java.util.regex API).";

        String expressao = "Post";

        Pattern regra = Pattern.compile(expressao);

        Matcher confrontador = regra.matcher(sentenca);

        while(confrontador.find()) {
            System.out.printf("Encontrado \"%s\" de %d a %d.%n",
                                           confrontador.group(),
                                           confrontador.start(),
                                            confrontador.end());
        }

        System.out.println("--------------------------------------");
    }
}

Saída:

--- Teste Com Expressões Regulares ---
Encontrado "Post" de 0 a 4.
--------------------------------------

Vamos ver o que fizemos em detalhes... ou em partes, como diria Jack, o estripador :D.
    1)Importamos os dois objetos que precisamos: Pattern e Matcher;
    2)Criamos duas strings, uma com uma sentença qualquer e uma com a nossa expressão a ser pesquisada;
    3)Depois criamos um Pattern com a expressão e um Matcher à partir do Patter passando como parâmetro a nossa sentença;
    4)Em um laço (enquanto forem encontradas ocorrências da nossa regra) imprimimos onde encontramos na sentença a nossa expressão (os índices). Notem que por convenção o índice das ocorrências sempre englobam mais uma posição (coisas da Sun). Faça o teste com a seguinte sentença postpostpost e na expressão use post:

    012345678901 índices
    postpostpost sentença

    Saída:

    --- Teste Com Expressões Regulares ---
    Encontrado "post" de 0 a 4. ??? Aaaaaaaaaaaaaah!
    Encontrado "post" de 4 a 8. ??? Aaaaaaaaaaaaaah!
    Encontrado "post" de 8 a 12. ??? Aaaaaaaaaaaaaah!
    --------------------------------------

Note que o índice de início da ocorrência seguinte é sempre o índice final da ocorrência anterior.

A API também da suporte uma série de caracteres especiais que alteram a forma como são coincididas as sentenças com a expressão.
Vamos testar isso usando agora a sentença: Eu tenho dois carros que trato com carinho e na expressão: carro. Resultado:

--- Teste Com Expressões Regulares ---
Encontrado "carro" de 14 a 19.
--------------------------------------

Agora usaremos de um dos n caracteres especiais suportados, o ponto final (.) em nossa expressão: car. (com ponto final). Resultado:

--- Teste Com Expressões Regulares ---
Encontrado "car." de 14 a 18.
Encontrado "car." de 35 a 39.
--------------------------------------

Note a diferença no resultado. O resultado é referente a carr e cari e não a car apenas (se fosse os índices seriam de 14 a 18 e de 35 a 38!). Para confirmar isso vamos mudar nosso programa. Onde usamos o comando printf vamos colocar ao invés de expressao o comando confrontador.group():

System.out.printf("Encontrado \"%s\" de %d a %d.%n",
                               confrontador.group(),
                               confrontador.start(),
                                confrontador.end());

Agora rodemos novamente o programa e a saída será:

--- Teste Com Expressões Regulares ---
Encontrado "carr" de 14 a 18.
Encontrado "cari" de 35 a 39.
--------------------------------------

Esses caracteres especiais como o ponto final (.) são chamados de meta caracteres – são caracteres com um significado especial para o interpretador.
Eis todos suportados pela API: ([{\^-$|]})?*+..
Alguns desses não podem ser usados sozinhos, pois devem formar uma parte da expressão (visto mais além). Também notem que !, @ e # nunca são meta caracteres.

Meta caracteres podem ser usados para formar-se conjuntos ao qual a sentença deva atender como regra geral.
Por exemplo, poderíamos ver se a sentença contem qualquer palavra com o prefixo pedr:
Expressão: pedr.*
Sentença: pedreiro Ok
Sentença: pedra Ok
Sentença: pedreira Ok
Sentença: pedal Nok

Agora você pergunta, ta mas eu tenho que criar minha regra sempre em maiúsculo e depois em minúsculo?
Claro que não, modificaremos nossa classe novamente:

Pattern regra = Pattern.compile(expressao, Pattern.CASE_INSENSITIVE);

Pronto, se agora passarmos: Pedra, PEDRA, PeDreiRa, etc... irá funcionar!

Com os meta caracteres você pode criar regras mais complexas, unir certas condições, especificas faixas de valor/ocorrências, etc... vejamos:

[a-c] Caracteres entre a e c (inclusive) ou [a-cA-C].
[a-c[e-z]] Qualquer caractere que não seja d.
[a-f&&[cd]] Somente c e d.
[^a-c] Qualquer caracteres que não esteja entre a e c.
[a-f&&[^cd]] Qualquer caracter entre a e f menos c e d.

Assim por diante...

Ainda há com verificar se há dígitos (\\d), caracteres que não sejam dígitos (\\D – o mesmo que [^0-9]) , etc... veja a lista completa na documentação da API.

Agora, na finaleira vamos ver se uma sentença tem apenas números. Para isso mudaremos nossa classe novamente. Onde tem o while vai ter o seguinte if:

if(confrontador.matches()) {
    System.out.printf("Entrada \"%s\" está conforme o padrão %s.%n",
                                                           sentenca,
                                                         expressao);
}
else {
    System.out.printf("Entrada \"%s\" não está conforme o padrão %s.%n",
                                                               sentenca,
                                                             expressao);
}

Agora a nossa expressão fica: \\d* e a nossa sentença: 01234 . Rode o exemplo, depois mude a sentença e assim por diante.

Bom o assunto é muito mais extenso que esse meu post, espero ter aguçado a curiosidade de vocês e que procurem na documentação da API mais informações assim como no Java Tutorial, etc...

Até a próxima pessoal.

Gostou? Não gostou? Achou algum erro? Comenta aí, não leva nem um minuto e me ajudará muito! :D

Aqui tem o post em pdf, para facilitar as coisas :P


http://geocities.yahoo.com.br/sadbuttruebr/blog/
posts_pdf/expressoes_regulares.pdf

terça-feira, maio 15, 2007

Trabalhando com arquivos .properties

Esses dias tive que manipular um arquivo .properties e não tinha muita noção de como o fazer, então fui dando uma fuçada aqui e ali e achei na API tudo que eu precisava e fiz o que eu queria.

Bom para efeitos didáticos e de salvamento, se alguém tiver no sufoco e tiver que fazer isso também, ta aí a classezinha que quebra o galho (na verdade não é a versão final do que eu fiz, mas daqui eu parti para o que eu precisava).

Primriro criei um arquivo de propriedades para testes. Arquivos de propriedades são aquivos contendo texto em que a cada linha se tem um par de chave e valor.

configuracoes.properties:

sistema=xyz
versao=0.0.1
plataforma=MS Windows
sgbd=MySQL
autor=Jean Jorge Michel

Agora eu queria algumas funcionalidades na minha classe (já que eu ia fazer um cara que pelo menos lesse o arquivo, eu já faria alterar e salvar também):

- public java.util.Properties carregarPropriedades(java.lang.String origem);
- public void imprimir(java.util.Map propriedades);
- public java.lang.String getPropriedade(java.util.Properties propriedades, java.lang.String propriedade);
- public java.util.Properties incluirPropriedade(java.lang.String chave, java.lang.String valor, java.util.Properties propriedades);
- public java.util.Properties removerPropriedade(java.lang.String chave, java.util.Properties propriedades);
- public void salvar(java.util.Map propriedades, java.lang.String destino).

Esses são os métodos que eu tenho na minha classe. Agora lá vem ela:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
* @author Jean Jorge Michel
* @version 1.0
*/
public class ManipuladorProperties {

    /**
     * Método que retorna um objeto Properties com as informações do sistema.
     * @param arquivo O Caminho físico do arquivo e seu nome.
     * @return Um objeto Properties.
     */
    public Properties carregarPropriedades(String origem) {
        FileInputStream arquivoConfig = null;

        Properties configSistema = new Properties();

        try {
            arquivoConfig = new FileInputStream(origem);

            configSistema.load(arquivoConfig);
        }
        catch(FileNotFoundException e) {
            e.printStackTrace();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                arquivoConfig.close();
            }
            catch(IOException e) {
                e.printStackTrace();
            }
        }

        return configSistema;
    }

    /**
     * Método que imprime no console todas as propriedades e seus respectivos
     * valores encontradas no arquivo de propriedades.
     * @param propriedades Um objeto do tipo Properties.
     */
    public void imprimir(Map propriedades) {
        Set<String> valores = propriedades.keySet();

        for(String valor: valores) {
            System.out.println(valor + "=" + propriedades.get(valor));
        }
    }

    /**
     * Método que retorna o valor de uma propriedade de acordo com o seu nome.
     * @param propriedades Um objeto do tipo Properties.
     * @param propriedade Nome da propriedade.
     * @return O valor da propriedade.
     */
    public String getPropriedade(Properties propriedades, String propriedade) {
        return propriedades.getProperty(propriedade);
    }

    /**
     * Método que adiciona ao objeto Properties mais uma propriedade.
     * @param chave O nome da nova propriedade a incluir.
     * @param valor O valor da nova propriedade
     * @param propriedades O objeto Properties a ser modificado.
     * @return Retorna o objeto Properties modificado.
     */
    public Properties incluirPropriedade(String chave,
                                         String valor,
                                         Properties propriedades) {
        propriedades.put(chave, valor);

        return propriedades;
    }

    /**
     * Método que remove do objeto Properties uma propriedade.
     * @param chave A propriedade a ser removida
     * @param propriedades O objeto Properties a ser modificado.
     * @return Retorna o objeto Properties modificado.
     */
    public Properties removerPropriedade(String chave, Properties propriedades) {
        propriedades.remove(chave);

        return propriedades;
    }

    /**
     * Método que persiste no disco o arquivo properties.
     * @param propriedades O objeto Properties a ser gravado.
     * @param destino O caminho físico e o nome do arquivo a ser criado.
     */
    public void salvar(Map propriedades, String destino) {
        FileWriter fw = null;
        
        try {
            fw = new FileWriter(destino);

            Set<String> valores = propriedades.keySet();

            for(String valor: valores) {
                fw.write(valor + "=" + propriedades.get(valor) + "\r\n");
            }
        }
        catch(FileNotFoundException e) {
            e.printStackTrace();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            }
            catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Para testar:

String caminho = "C://temp//";
String arquivo = "configuracoes.properties";

ManipuladorProperties mp = new ManipuladorProperties();
Properties configSistema = mp.carregarPropriedades(caminho + arquivo);

//Lista as propriedades
mp.imprimir(configSistema);

//Recupera uma propriedade específica
System.out.println(mp.getPropriedade(configSistema, "autor"));

//Inclui uma propriedade nova
mp.incluirPropriedade("data", new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date()), configSistema);

//Grava o arquivo, neste caso com outro nome
mp.salvar(configSistema, caminho + "novas_propriedades.properties");

É isso aí, esta feito quem quiser usar pode usar, sem nenhuma garantia claro. :)

Gostou, quer criticar, sugerir algo... comente aí o post!

sexta-feira, maio 11, 2007

Annotations - Primeiro contato

Dae, tudo tranquilo?
Bom esses dias (depois de ter dado uma estudada em threads) andei dando uma aprofundada em Annotations.

Pó que saco, tudo que era lugar que eu lia sobre dizia a mesma coisa: anotações são meta-dados que blablabla... E uns exemplos muito complexos pra quem tava querendo entender para que um dia EU usaria essa funcionalidade do Java.v

Bem, então... hã... hun... pensei, pra mim ver para que serve tenho que fazer um exemplo, seja ele bizarro ou real eu preciso fazer. Então defini o seguinte:

Um POJO chamado Cliente (que original!);
Uma annotation chamada Validar;
Uma classe que interaja com meu POJO chamada CadastroCliente;
Uma classe que use dos campos anotados do meu POJO fazendo alguma coisa, neste caso validando-o.

Então o seguinte, annotations nada mais são do que meta-dados que estão nas classes e não servem pra pombas nenhuma, a não ser que alguém saiba o que fazer com eles. Exemplos clássicos são o modificador transient, que é uma anotação que informa ao mecanismo de serialização que o campo não deve ser serializado, a tag @deprecated do Java Doc, que indica que um método ira ser descontinuado em versões futuras da classe, e por aí vai. A vantagem que agora o desenvolvedor pode criar as suas anotações.

Bem o que meu exemplo faz é o seguinte, todo campo no meu POJO que tiver marcado com a anotação @Validar vai ser validado por um mecanismo através de reflection (papo para ouuuuutro post :P ).

Então criei a anotação, que nada mais é do que uma “interface” com @ antes da palavra reservada interface e que eu posso definir um nível de atuação para ela, no meu casso campos (poderia ser método, classe, etc... veja na API java.lang.annotation.ElementType os tipos permitidos) e que será usada em tempo de execução (também há uma lista de valor fixos na API java.lang.annotation.RetentionPolicy):

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Validar {
}

Depois disso criei meu POJO, e disse quais os campos dele serão validados:

public class Cliente {

    @Validar
    private String nomeCompleto;

    @Validar
    private int idade;
    
    private String email;

    public String getNomeCompleto() {
        return nomeCompleto;
    }

    public void setNomeCompleto(String nomeCompleto) {
        this.nomeCompleto = nomeCompleto;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Cliente() {
    }

    public Cliente(String nome, int idade) {
        this.nomeCompleto = nome;
        this.idade = idade;
    }

    public Cliente(String nome, int idade, String email){
        this.nomeCompleto = nome;
        this.idade = idade;
        this.email = email;
    }
}

Note que o campo que armazenará o e-mail do cliente não será validado, poderá ser nulo ou qualquer texto inválido para endereço eletrônico.

Agora sim, meu validador que vai pegar o objeto ver seus campos, ver os que estão “anotados” e fazer algumas validações:

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class ValidadorCadastro {
    public static String nvl(String entrada) {
        String saida = "";

        if(entrada == null) {
            saida = "";
        }
        else {
            saida = entrada;
        }

        return saida;
    }

    public List<String> validarCadastro(Cliente c) throws IllegalAccessError, Exception {

        Field[] fields = c.getClass().getDeclaredFields();

        String nomeCliente = null;

        Integer idade = null;

        List<String> erros = new ArrayList<String>();

        for (Field field : fields) {
            if (field.isAnnotationPresent(Validar.class)) {

                field.setAccessible(true);

                if(field.getName().equals
("nomeCompleto")) {

                    nomeCliente = (String)field.get(c);
                    if(nvl(nomeCliente).length() < 4) {
                        erros.add("O nome do cliente deve ter mais " +
                                  "de 4 caracteres.");
                    }
                }

                if(field.getName().equals("idade")) {
                    idade = (Integer)field.get(c);
                    if(idade < 18) {
                        erros.add("O cliente deve ser maior de idade.");
                    }
                }

                if (field.get(c) == null) {
                    erros.add("Campo [" + field.getName() + "] não " +
                              "preenchido.");
                }
            }
        }

        return erros;
    }
}

E uma classe simples que interaja com o POJO e o validador:

import java.util.List;

public class CadastroCliente {

    public static void main(String[] args) {
        Cliente c0 = new Cliente("Jean Jorge Michel", 25);

        List<String> errosCadastro = null;

        ValidadorCadastro vc = new ValidadorCadastro();

        try {
            errosCadastro = vc.validarCadastro(c0);

            if(! errosCadastro.isEmpty()) {
                for(String erroOcorrido : errosCadastro) {
                    System.out.println(erroOcorrido);
                }
            }
            else {
                System.out.println("Cadastro concluído com sucesso!");
            }
        }
        catch(IllegalAccessError e) {
            e.printStackTrace();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }
}

Rodando a classe assim como está não há problemas, vai passar na validação... agora troque o nome, diminua a idade, não informe o nome... e a lista de problemas encontrados será exibida.

Bom, é uma aplicação para as anotações mas claro que há outras, como configuração de frameworks por exemplo. Veja o Hibernate annotation :).

Era isso, de tema para casa fica validar o campo e-mail também :D.

Gostou, não gostou, achou um erro, quer me xingar: comente o post!