Java for Dinner

Um repositório sobre Java e tudo que gira ao redor dele.

O poder das annotations em sistemas legados

Posted by Danilo Barsotti em 9 março, 2008

odos sabem como é complicado mexer com sistemas legados, ainda mais quando esse sistema é um dos principais de um grande banco brasileiro e com isso todo cuidado é pouco para não ultrapassar os prazos.

As Annotations:

Dentre várias novidades do java 1.5 uma delas foi as Annotations, elas nos dão a possibilidade de se trabalhar com meta-dados muito facilmente, com pouco esforço podemos fazer varias coisas que antes não poderiamos ou então era muito trabalhoso.

No projeto atual, dentre várias tarefas que tenho, uma delas era de incluir algumas validações ( + ou – 400 ) no sistema, pela quantidade de validações e pelo curto prazo para cumprir a tarefa, tive que analisar o impacto que isso poderia acarretar.

Conclusão, era inviavel colocar um monte de código novo para fazer essas validações, primeiro porque ia ser muito trabalhoso e segundo que era muito arriscado.

Então fiz algumas classes ( que alguns chamam de framework, rsrs ) que gerenciam anotações para fazer todo meu trabalho de validação de variaveis, entrada de parametros em métodos, retorno de métodos ( ai usei AOP ) e por ai vai.

Então, vamos ao que interessa!

Obs.: Como esse projeto já é usado pelo banco e por motivos de segurança, vou mostrar simples validações e não as que foram usadas no banco, mas o motor é o mesmo, somente a logica das validações foram retiradas.

Mão na massa

Quando comecei a programar essa framework, umas das coisas que eu tinha em mente era ela ser rápida e flexivel na programação das anotações.

Comecei programando o motor de gerenciamento das anotações, a classe esta muito bem comentada sendo assim, acho q dispensa maiores explicações:

/**
* Classe responsavel por gerenciar o funcionamento de todas as annotations
* usadas pela framework.
*
* Para cada anotação encontrada, é pego o valor do atributo onde a anotação
* foi encontrada e esse valor é guardado em um HashMap, quando todos os
* atributos forem lidos e todas as annotations também, são disparadas as
* Threads para cada anotação ou conforme configuração do framework.
*
* @author danilo barsotti
*/
public class Validators extends Control {

private Configuration mainConf;

public Validators(Configuration conf){
if(conf.getMapConf() == null){
mainConf = conf.initDefaultConfiguration();
}else{
mainConf = conf.getInstance();
}
}

/**
* Inicia o gerenciamento dos validadores ( threads )
* disparando uma thread para cada annotation ou 1 thread
* para todas as annotations ou um numero especifico de threads
* conforme a configuração do framework.
*
* @author Danilo Barsotti
*/
public void initValidators(Object annotedObject) {
ArrayList<ValidatorException> exceptions = new ArrayList<ValidatorException>();
// Map<nomeDaAnotação, HashMap<nomeDoAtributo, valorDoAtributo>>
Map<String, HashMap<String, Object>> mapFieldUsedAnnotation = new HashMap<String, HashMap<String, Object>>();

Map<String, HashMap<String, Annotation[]>> mapFieldColAnnotation = new HashMap<String, HashMap<String, Annotation[]>>();

List<String> nameAnnotations = new ArrayList<String>();
// pool de threads
ExecutorService pool = Executors.newCachedThreadPool();
Validator validator = null;

Class<Annotation> anottation = null;

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

Annotation[] colAnnotations = null;

for (Field field : fields) {
colAnnotations = field.getDeclaredAnnotations();
// Separa os atributos para cada anotação que esta sendo usado
for (Annotation annotation : colAnnotations) {
// Verifica se já existe no map algum atributo que já tenha
// usado a anotação
if (mapFieldUsedAnnotation.containsKey(annotation
.annotationType().getSimpleName())) {
mapFieldUsedAnnotation.get(
annotation.annotationType().getSimpleName()).put(
field.getName(),
super.getValueAtribute(super.getMethodName(field
.getName()), annotedObject.getClass()));
// acrescenta no map todas as anotações usadas pelo
// atributo.
mapFieldColAnnotation.get(
annotation.annotationType().getSimpleName()).put(
field.getName(), colAnnotations);
// Nenhum atributo usou a anotação até agora, cria um novo
// map e adiciona o valor
} else {
mapFieldUsedAnnotation.put(annotation.annotationType()
.getSimpleName(), new HashMap<String, Object>());
mapFieldUsedAnnotation.get(
annotation.annotationType().getSimpleName()).put(
field.getName(),
super.getValueAtribute(super.getMethodName(field
.getName()), annotedObject.getClass()));
nameAnnotations.add(annotation.annotationType()
.getSimpleName());
// Todas as anotações usadas pelo atributo.
mapFieldColAnnotation.put(annotation.annotationType()
.getSimpleName(),
new HashMap<String, Annotation[]>());
mapFieldColAnnotation.get(
annotation.annotationType().getSimpleName()).put(
field.getName(), colAnnotations);
}
}
}
// Inicia as Threads
try {
for (String annotationTemp : nameAnnotations) {
// TODO mudar o valor default de onde se encontram os validator, talvez pegar de um .properties
// pega a thread com base no nome da annotation
validator = (Validator) Class.forName(
“control.validator.validators.Validator”
+ annotationTemp).newInstance();
// pega uma instancia da classe que representa a anotação
anottation =  (Class<Annotation>) Class.forName(“annotations.” + annotationTemp);
// seta um map com os valores dos atributos a serem validados
validator.setObjToValidade(mapFieldUsedAnnotation
.get(annotationTemp));
// seta um map com uma coleção de anotações usadas pelo field
validator.setMapFieldColAnnotation(mapFieldColAnnotation
.get(annotationTemp));
// seta uma instancia da anotação
validator.setClazz(anottation);
// seta onde as exceptions serão armazenadas
validator.setExceptions(exceptions);
// seta quais são os campos a serem validados
validator.setFieldsToValidade();
// inicia um validador pegando uma Thread do pool
pool.execute(validator);
}
pool.shutdown();

while(true){
if(pool.isTerminated()){
System.out.println(“acabou!!!”);
break;
}
}
} catch (ClassNotFoundException e) {
// TODO Lançar a exeção correta
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Lançar a exeção correta
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Lançar a exeção correta
e.printStackTrace();
}
}

}

/**
* Classe responsavel por métodos utilitarios em que todas
* as classes que controlam as annotations devem usar.
*
* @author Danilo Barsotti
*/
public abstract class Control {

public abstract void initValidators(Object annotedObject);

/**
* Retorna o nome do método acessor do atributo seguindo o
* padrão JavaBean ( getCamelCase ).
*
* @param atributeName – nome do atributo
* @return nomenclatura do método acessor do atributo, seguindo o padrão JavaBean
*
* @author Danilo Barsotti
*/
protected String getMethodName(String atributeName) {
return “get”+atributeName.replaceFirst(atributeName.substring(0,1), atributeName.substring(0,1).toUpperCase());
}

/**
* Invoca o método usando reflection para pegar o valor de
* retorno.
*
* @param methodName – nome do método a ser invocado
* @param clazz – a qual classe o método pertence
* @return return do método acessor.
*
* @author Danilo Barsotti          */
protected Object getValueAtribute(String methodName, Class clazz) {
Object objReturn = null;
try {
if(!clazz.getMethod(methodName).isAccessible()){
clazz.getMethod(methodName).setAccessible(true);
}
Method method = clazz.getMethod(methodName);
objReturn = method.invoke(clazz.newInstance());
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return objReturn;
}
}

Pronto, o motor de gerenciamento já está pronto, como podem ver é bem simples, ele dispara uma thread para cada anotação diferente encontrada nas classes anotadas.

Agora vamos ver uma anotação.

/**
* Verifica se o atributo anotado está nulo.
* Caso o parametro <b>acceptEmpty</b> seja false, não aceita
* uma String vazia “”, <b>valor default é true</>
*
* @param acceptEmpty – <b>Opcional</b>
* @param message – a mensagem que sera exibida caso a validação
* seja incorreta.
*
* @author danilo barsotti
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {

boolean acceptEmpty() default true;
String message() default “”;

}

Essa anotação verifica se a variavel ( field ) está nula e se for uma String pergunta se aceita vazio (“”) ou não.

Usando convenção ao invez de configuração, a classe que representa a lógica dessa anotação deve ter o nome no seguinte padrão: Validator + O nome da anotação, então ficaria ValidatorNotNull, com isso conseguimos fazer varias anotações sem precisar configurar nada, somente precisamos fazer 1 annotation e 1 classe que representa sua lógica de validação e então o framework já vai conseguir usa-la.

A classe que representa a regra de validação da anotação:

/**
* Verifica se o atributo é Null
*
* @author danilo barsotti
*/
public class ValidatorNotNull extends Validator{

@Override
public void startValidation() throws ValidatorException  {
for(String fieldName : fields){
super.setFieldName(fieldName);
if(isNull(fieldValue)){
System.out.println(“O atributo ” + fieldName + ” é nulo!”);
}else{
if(getBooleanValueMethodAnnotation(fieldName, “acceptEmpty”)){
System.out.println(“O atributo ” + fieldName + ” Não é nulo! o valor dele é = ” + getValueToValidade(fieldName));
}else{
if(fieldValue.equals(“”)){
System.out.println(“O atributo ” + fieldName + ” Não é nulo mas é uma String vazia”);
addValidationError(fieldName);
}
}
}
}
}

/**
* Verifica se o objeto é nulo
*
* @param objeto a ser verificado
* @return boolean
*
* @author danilo barsotti
*/
public boolean isNull(Object obj){
return obj == null;
}
}

Como podem ver, toda classe que representa uma anotação deve estender a classe Validator, ela é uma classe que implementa Runnable e também disponibiliza alguns métodos utilitarios.

Classe Validator

/**
* Classe que todos os outros validadores devem estender
* pois é por ele que a thread acessa os atributos a serem
* validados de acordo com a annotation que a thread representa.
*
* @author danilo barsotti
*
*/
public abstract class Validator implements Runnable{
//Logger logger;
Object lock1 = new Object();
private String fieldName;
public Object fieldValue;
public String[] fields;
/**
* Classe que representa uma anotação.
*/
private Class<Annotation> clazz;
/**
* Onde todas as exceptions de validação são guardadas
*/
ArrayList<ValidatorException> exceptions;
/**
* Guarda os valores dos atributos a serem validados
*/
private HashMap<String, Object> objToValidade;

/**
* Guarda todas as anotações usadas pelo atributo
*/
private HashMap<String, Annotation[]> mapFieldColAnnotation;
/**
* Método que inicia a validação
*
* @author danilo barsotti
* @throws ValidatorException
* @throws ValidatorException
*/
public abstract void startValidation() throws ValidatorException;

/**
* Seta a anotação que é usada pelo validador.
*
* @param clazz – Classe que representa a anotação
*/
public void setClazz(Class<Annotation> clazz) {
this.clazz = clazz;
}

/**
* Retorna os nomes dos campos que foram anotados.
*
* @return keys
*
* @author danilo barsotti
*/
public String[] getFieldsToValidade(){
String[] keys = new String[objToValidade.keySet().size()];
Iterator<String> iter = objToValidade.keySet().iterator();
int cont = 0;
while(iter.hasNext()){
keys[cont] = iter.next().toString();
cont++;
}
return keys;
}
/**
* ESSE MÉTODO NÃO DEVE SER SOBRESCRITO E NÃO DEVE SER USADO.
* É USADO SOMENTE PELO FRAMEWORK.
* <br>
* Seta os nomes dos atributos a serem validados.
*/
public void setFieldsToValidade(){
this.fields = getFieldsToValidade();
}
public void addValidationError(String fieldName){
synchronized (lock1) {
exceptions.add(new ValidatorException(fieldName));
}
}
/**
* Retorna um HashMap que contem os objetos a serem validados
*
* @return HashMap com os objetos
*
* @author danilo barsotti
*/
public HashMap<String, Object> getObjToValidade() {
return objToValidade;
}
/**
* Retorna o valor do atributo a ser validado
*
* @param key – Nome do atributo (field)
*
* @return object – valor retornado pelo método
*/
public Object getValueToValidade(String key) {
return getObjToValidade().get(key);
}
/**
* ESSE MÉTODO NÃO DEVE SER SOBRESCRITO E NÃO DEVE SER USADO.
* É USADO SOMENTE PELO FRAMEWORK.
*
* Seta os objetos a serem validados
*
* @param objToValidade – map com os objetos a serem validados
*
* @author danilo barsotti
*/
public void setObjToValidade(HashMap<String, Object> objToValidade) {
this.objToValidade = objToValidade;
}

/**
* ESSE MÉTODO NÃO DEVE SER SOBRESCRITO E NÃO DEVE SER USADO.
* É USADO SOMENTE PELO FRAMEWORK.
*
* Da inicio a Thread de validação conforme a implementação do validador.
*
* @author danilo barsotti
*/
public void run(){
try{
startValidation();
}catch(ValidatorException ex){
synchronized (lock1) {
exceptions.add(ex);
}
}
}

/**
* Retorna a anotações que foi usada pelo field.
*
* @param fieldName – nome do field
*
* @return Annotation
*
* @author danilo barsotti
*/
public Annotation getAnnotation(String fieldName) {
Annotation[] annotations = mapFieldColAnnotation.get(fieldName);
for(Annotation annotation : annotations){
if(annotation.annotationType().getSimpleName().equals(clazz.getSimpleName())){
return annotation;
}
}
// TODO lançar uma excessão aqui, pois não encontrou a anotação!
//logger.log(Level.WARNING, “asdf”);
return null;
}
/**
* Usado para pegar o valor (Integer) de retorno do método que foi declarado
* na anotação
*
* @param fieldName – nome do atributo anotado
* @param nameMethod – nome do método na anotação
*
* @return Integer com o valor retornado pelo método na anotação
*
* @author danilo barsotti
*/
public Integer getIntegerValueMethodAnnotation(String fieldName, String nameMethod){
// TODO verificar se o clazz é uma instancia de Annotation.class
return  (Integer) getMethodValue(fieldName, nameMethod);
}

/**
* Usado para pegar o valor (boolean) de retorno do método que foi declarado
* na anotação
*
* @param fieldName – nome do atributo anotado
* @param nameMethod – nome do método na anotação
*
* @return boolean com o valor retornado pelo método na anotação
*
* @author danilo barsotti
*/
public boolean getBooleanValueMethodAnnotation(String fieldName, String nameMethod){
return (Boolean) getMethodValue(fieldName, nameMethod);
}
/**
* Usado para pegar o valor (boolean) de retorno do método que foi declarado
* na anotação
*
* @param fieldName – nome do atributo anotado
* @param nameMethod – nome do método na anotação
*
* @return boolean com o valor retornado pelo método na anotação
*
* @author danilo barsotti
*/
public String getStringValueMethodAnnotation(String fieldName, String nameMethod){
return (String) getMethodValue(fieldName, nameMethod);
}
/**
* Usado para pegar um valor de retorno de um método que foi declarado
* na anotação, pega qualquer valor de retorno mas necessita de cast
* pois retorna um object.
*
* @param fieldName – nome do atributo anotado
* @param nameMethod – nome do método na anotação
*
* @return object – valor retornado pelo método
*/
private Object getMethodValue(String fieldName, String nameMethod){
// TODO verificar se o clazz é uma instancia de Annotation.class
Object obj = null;
try {
Annotation annotation = getAnnotation(fieldName);
obj = annotation.getClass().getMethod(nameMethod).invoke(annotation);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}

/**
* ESSE MÉTODO NÃO DEVE SER SOBRESCRITO E NÃO DEVE SER USADO.
* É USADO SOMENTE PELO FRAMEWORK.
*
* Seta um map que contem as anotações usadas pelos fields.
*
* @param objToValidade – map com as anotações
*
* @author danilo barsotti
*/
public void setMapFieldColAnnotation(
HashMap<String, Annotation[]> mapFieldColAnnotation) {
this.mapFieldColAnnotation = mapFieldColAnnotation;
}

public ArrayList<ValidatorException> getExceptions() {
return exceptions;
}

public void setExceptions(ArrayList<ValidatorException> exceptions) {
this.exceptions = exceptions;
}

public void setFieldName(String fieldName) {
this.fieldName = fieldName;
this.fieldValue = getValueToValidade(fieldName);
//this.fields = getFieldsToValidade();
}
}

Quando o validador encontra algo que não é aceito, ele lança uma exeção e coloca ela dentro de um array.

Segue a classe que representa uma exeção de validação:

public class ValidatorException extends Exception{

String field;
public ValidatorException(String field){
System.out.println(“—– rolou uma exception no campo = ” + field + ” —–”);
this.field = field;
}
public void teste(){
}
}

Também foi feito uma classe para fazer algumas configurações na framework, também não posso mostrar muito dela então tirei varias coisas bacanas, mas vamos talvez no proximo artigo focar nela e também em AOP, para deixar nossa framework mais interessante.

Classe de configuração:

public class Configuration {

private static Configuration config;
private static Map<String, String> mapConf;
private Configuration(){}
public static Configuration getInstance(){
if(config == null){
config = new Configuration();
mapConf = new HashMap<String, String>();
}
return config;
}
public static Configuration setConfig(PropertiesConfiguration conf, String value){
getInstance();
mapConf.put(conf.toString(), value);
return config;
}
public static String getConf(PropertiesConfiguration key){
return mapConf.get(“”);
}
public static Map getMapConf(){
return mapConf;
}
public static Configuration initDefaultConfiguration(){
getInstance().getMapConf().put(PropertiesConfiguration.Threads, “”);
return getInstance();
}
}

E uma enum com as propriedades da configuração, só tem uma configuração pelo mesmo motivo já citado diversas vezes.

Classe PropertiesConfiguration:

public enum PropertiesConfiguration {

Threads;

}

Com isso finalizamos nossa framework, agora para testar basta anotar alguma classe e chamar o método de validação, no banco fiz isso ficar automatico, sem precisar chamar nenhum método a framework já verifica se tem algo de errado, lança suas exeções, joga tudo na request e exibe em uma pagina que também é configuravel, mas isso é para um próximo artigo.

Para testar vamos anotar um VO:

public class TesteVo {

@NotNull(acceptEmpty=false)
public String testeNotNull = “”;
@NotNull
public String testeNotNull2 = null;
public String testeNotNull3 = “valorTesteNotNull3″;
@NotNull
public String range = “valorRange”;
@NotNull
public Integer testeNotNull4;
@NotNull
public Integer testeNotNull5 = 0;
@NotNull
public Integer testeNotNull6 = 211;
@NotNull
public int testeNotNull7 = 1;
@NotNull
public Object testeNotNull8 = null;

public String getTesteNotNull() {
return testeNotNull;
}

public void setTesteNotNull(String testeNotNull) {
this.testeNotNull = testeNotNull;
}

public String getRange() {
return range;
}

public void setRange(String range) {
this.range = range;
}

public String getTesteNotNull2() {
return testeNotNull2;
}

public void setTesteNotNull2(String testeNotNull2) {
this.testeNotNull2 = testeNotNull2;
}

public String getTesteNotNull3() {
return testeNotNull3;
}

public void setTesteNotNull3(String testeNotNull3) {
this.testeNotNull3 = testeNotNull3;
}

public Integer getTesteNotNull4() {
return testeNotNull4;
}

public void setTesteNotNull4(Integer testeNotNull4) {
this.testeNotNull4 = testeNotNull4;
}

public Integer getTesteNotNull5() {
return testeNotNull5;
}

public void setTesteNotNull5(Integer testeNotNull5) {
this.testeNotNull5 = testeNotNull5;
}

public Integer getTesteNotNull6() {
return testeNotNull6;
}

public void setTesteNotNull6(Integer testeNotNull6) {
this.testeNotNull6 = testeNotNull6;
}

public int getTesteNotNull7() {
return testeNotNull7;
}

public void setTesteNotNull7(int testeNotNull7) {
this.testeNotNull7 = testeNotNull7;
}

public Object getTesteNotNull8() {
return testeNotNull8;
}

public void setTesteNotNull8(Object testeNotNull8) {
this.testeNotNull8 = testeNotNull8;
}
}

E chamar a validação para testar o resultado:

public class AnnotationsTestMain {

public static void main(String args[]){
Control teste = new Validators(Configuration.setConfig(PropertiesConfiguration.Threads, “1″));
TesteVo t = new TesteVo();
teste.initValidators(t);
}

}

A saida no console foi:

O atributo testeNotNull4 é nulo!
O atributo testeNotNull6 Não é nulo! o valor dele é = 211
O atributo testeNotNull Não é nulo mas é uma String vazia
—– rolou uma exception no campo = testeNotNull —–
O atributo testeNotNull5 Não é nulo! o valor dele é = 0
O atributo range Não é nulo! o valor dele é = valorRange
O atributo testeNotNull2 é nulo!
O atributo testeNotNull7 Não é nulo! o valor dele é = 1
O atributo testeNotNull8 é nulo!
acabou!!!

Com isso chegamos ao fim da nossa pequena mas pratica framework de validação, os proximos passos são, torna-la mais configuravel, fazer mais validadores, usar AOP, colocar as exeções na request para ser exibidas em um jsp e por ultimo junta-la com a framework Apache Wicket.

Não estou tendo tempo de postar, esta muito corrido meu projeto, mas vou ver se consigo arrumar tempo.

Um grande abraço a todos, e até a proxima!!! :- )

Posted in Annotation | 4 Comments »

Mudando o local default dos arquivos no Apache Wicket

Posted by Danilo Barsotti em 28 janeiro, 2008

Como foi falado no artigo anterior, quando criamos uma classe que representa uma pagina ( estende WebPage ) o arquivo .html deve estar no mesmo package da classe por default, mas isso nem sempre ( na maioria das vezes ) é algo muito bom de se fazer, pois um projeto pode ter diversos packages, imagine varios deles com arquivos .html, imagens, .js entre outros, não ficaria algo muito agradavel de se trabalhar e organizar.

Como não gostava disso fui dar uma pesquisada e descobri que quando se quer configurar como o wicket usa os recursos ( .html, .css, .js entre outros ) basta estender a classe ResourceStreamLocator, vejam como essa classe é feita:

import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;

public class PathStripperLocator extends ResourceStreamLocator{

    public PathStripperLocator() {
    }

    public IResourceStream locate(final Class clazz, final String path) {
        IResourceStream located = super.locate(clazz, trimFolders(path));
        if (located != null) {
            return located;
        }
        return super.locate(clazz, path);
    }

    private String trimFolders(String path) {
        return path.substring(path.lastIndexOf(“/”) + 1);
    }
}

E para usala, é só chamar no método init() da classe que estende WebApplication que no nosso caso é a classe JavaForDinnerApplication ( caso você não esteja acompanhado os artigos, recomendo a leitura do artigo Começando com o Apache Wicket ) como é mostrado abaixo:

    @Override
    protected void init() {
        super.init();
        this.configResourceFolder();
    }
    private void configResourceFolder(){
        IResourceSettings resourceSettings = getResourceSettings();
        resourceSettings.addResourceFolder(“/WEB-INF/”); // O local onde os arquivos .html,imagens e todo o restante vão estar.
        resourceSettings.setResourceStreamLocator(new PathStripperLocator());       
    }

Como podem ver, configurei para os arquivos .html, .css, .js e todos os outros necessarios vão ser encontrados na pasta WEB-INF.

Até a proxima!!!

Posted in Wicket | 4 Comments »

Começando com o Apache Wicket.

Posted by Danilo Barsotti em 26 janeiro, 2008

Bom pessoal, hoje vou dar inicio a uma serie de posts sobre essa framework, o Apache Wicket.
Wicket é uma framework web ( como o Struts, JSF, Mentawai entre outros 859664 milhões de frameworks ) que possui um estilo de programação “ala” Swing, possui uma curva de aprendizado bem pequena, é prazeroso e usa pouca configuração em .xml para que o programa funcione perfeitamente.
Aqui segue um lista dos pontos principais do Wicket:

1. Utiliza POJO ( http://pt.wikipedia.org/wiki/Plain_Old_Java_Objects ).
2. O código é escrito de uma forma “ala” Swing.
3. Resolve os problemas do botão voltar do navegador automaticamente.
4. Mensagens de erros fáceis de se entender e corrigir.
5. Os componentes escritos utilizando o Wicket são totalmente reutilizáveis e podem ser distribuídos em um .jar .
6. Não é intrusivo
7. Compatível com qualquer editor HTML.
8. Entre outras coisas… ( http://wicket.apache.org/introduction.html )

Agora depois de tantas apresentações, vou começar a demonstrar o poder dessa framework.
Eu sei que todo site e blog mostra como iniciar um projeto do zero utilizando o wicket, e eu não vou ser diferente pois acho que apesar de se encontrar essa informação em todo lugar, no javaForDinner não existe, sendo assim:
Você pode criar o seu projeto de 2 maneiras, a primeira é utilizando o Maven e a segunda é fazer na unha, vamos na unha mesmo.

O primeiro passo é fazer o download do wicket , SLF4J e do Log4j. E caso você ainda não tenha um container sugiro o tomcat ou jetty, nesse artigo eu uso o tomcat.
Depois que os downloads foram concluídos, abra o .zip do wicket, entre na pasta lib e adicione o arquivo wicket-1.3.0.jar em seu projeto, ele é a lib principal, onde se encontra todo o core do wicket e todo o mínimo necessário para que se consiga fazer um sistema.
Abra o .zip do SLF4J e copie os arquivos slf4j-log4j12-1.4.2.jar e slf4j-api-1.4.2.jar para o seu projeto.
Abra o .zip do Log4j e copie o arquivo log4j-1.2.15.jar para o seu projeto também.
Pronto, todos as libs necessárias para iniciarmos nosso projeto já estão prontas. Não vou mostrar como adicionar essas libs em seu projeto web pois isso é uma coisa comum que a maioria das pessoas conhecem e também para não sairmos do foco do artigo.
Dentro da pasta WebContent /WEB-INF crie/edite o arquivo web.xml com as seguintes informações:


<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4"><display-name>JavaForDinner</display-name>
<context-param>
<param-name>configuration</param-name>
<param-value>development</param-value>
</context-param>
<filter><filter-name>javafordinner</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>br.com.javafordinner.JavaForDinnerApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>javafordinner</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>

Pronto, essa é a unica configuração que é feita atravéz de um xml, o restante agora é só java :)
Crie uma classe com o nome de JavaForDinnerApplication no pacote br.com.javafordinner e escreva o código a seguir nela:


public class JavaForDinnerApplication extends WebApplication
{
/**
* Constructor
*/
public JavaForDinnerApplication()
{
   super();
}

public Class getHomePage()
{
   return Index.class;
}

@Override
protected void init() {
   super.init();
}

}

Essa classe representa a sua aplicação como se fosse um “public static void main(String args[]) em uma aplicação desktop, é ela que da o start e configura a aplicação.
Vou explicala em detalhes:

WebApplication
A classe que representa a aplicação tem que estender a WebApplication, pois quando iniciamos o servidor, ele vai procurar pela classe que declaramos no web.xml, a classe que representa a aplicação.

public Class getHomePage()
Vou mostrar o codigo da classe Index a seguir, mas por hora, essa classe representa a pagina principal da aplicação, que no caso, é a Index.html.

protected void init()
Aqui setamos todas as configurações necessarias em nossa apliacação, como setar o local padrão dos arquivos css, html entre outras coisas. No momento não existe nenhuma configuração especial, então
só é chamado o super.init() para gerar a configuração default.

Agora vamos criar a classe que representa nossa pagina inicial, crie uma classe chamada Index no pacote br.com.javafordinner.view.pages.Index e escreva o código a seguir:


/**
* Homepage
*/
public class Index extends WebPage {

private static final long serialVersionUID = 1L;

public Index(final PageParameters parameters) {
   add(new Label("label", "Olá Java for Dinner!!!"));
}

}

Explicando:

Toda classe que representa uma pagina da web deve extender a classe WebPage.
Quando seu construtor é criado recebendo paramentros, isso indica ao Wicket que essa pagina não usa a session do servidor por default.

Agora crie um html no mesmo pacote da classe Index ( br.com.javafordinner.view.pages ) com o nome de Index.html e escreva o código a seguir:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<span wicket:id="label"></span>
</body>
</html>

Reparem que exite um span com uma marcação “wicket:id=”label”, esse id=”label” representa o Label da classe Index.

Veja a imagem abaixo o resumo:

Clique aqui e veja uma imagem resumindo o fluxo que seguimos.

O label no html vai ser substituído pelo “Olá Java for Dinner!!!” da classe Index.Agora é só iniciar o servidor e acessar o endereço http://localhost:8080/javafordinner/ e pronto, você ira ver a mensagem .

Nos próximos artigos vamos nos aprofundar mais montando nossos componentes customizados, mudar o comportamento do Wicket entre outras coisas.

Até a próxima!!

Posted in Wicket | Etiquetado: , | 6 Comments »

Sobre

Posted by Danilo Barsotti em 25 novembro, 2007

Esse blog tem o objetivo de dar dicas, idéias e soluções usando a linguagem Java e tudo oque ela pode oferecer.

Posted in Não classificado | Leave a Comment »

 
Seguir

Obtenha todo post novo entregue na sua caixa de entrada.