Pular para o conteúdo

Iniciando com Apache Wicket e Java

Postado em 6 minutos de leitura

Instalação

Há várias maneiras de instalar o Wicket mas vou abordar a mais simples que é através do Maven:

mvn archetype:create \
-DarchetypeGroupId=org.apache.wicket \
-DarchetypeArtifactId=wicket-archetype-quickstart \
-DarchetypeVersion=1.4.16 \
-DgroupId=net.gustavohenrique \
-DartifactId=introwicket \
-DinteractiveMode=false

cd introwicket
mvn eclipse:eclipse

Os comandos acima servem para fazer o download a partir dos repositórios do Maven e criar a estrutura do projeto compatível com o eclipse. Depois é preciso importar o projeto no eclipse (File\Import\General\Existing Projects into Workspace).

Uma vez importado, nos deparamos com um erro na IDE acusando que não foi possível encontrar o diretório M2_REPO. Para corrigir basta criar a variável M2_REPO em Java Build Path apontando para o repositório local do Maven (geralmente $HOME/.m2/repository).

Configuração Inicial

Esse exemplo criado através do Maven possui 3 arquivos: WicketApplication.java, HomePage.java e HomePage.html. O primeiro é uma classe que extende de WebApplication, responsável pelas configurações do Wicket tal como diretório dos HTMLs, montagem de URLs, etc. Os outros 2 constituem um exemplo simples de página.

Uma página é uma classe que extende de WebPage e define via construtor quais os componentes inseridos via Java. O nome do arquivo HTML deve ter o mesmo nome da classe Java.

Por padrão, o Wicket procura o HTML da página no mesmo pacote da classe. Manter os .html junto aos .java é horrível e deixa o projeto desorganizado. Felizmente há como mudar isso.

Abra o WicketApplication.java e faça alguns ajustes na configuração:

package net.gustavohenrique;
 
import java.net.MalformedURLException;
import java.net.URL;
 
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.protocol.http.SecondLevelCacheSessionStore;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.pagestore.DiskPageStore;
import org.apache.wicket.session.ISessionStore;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.UrlResourceStream;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;
import org.apache.wicket.util.string.Strings;
 
public class WicketApplication extends WebApplication {
    public void init() {
        getMarkupSettings().setDefaultMarkupEncoding("UTF-8");
        getResourceSettings().setResourceStreamLocator(new MyOwnStreamLocator());
    }  
 
    public Class getHomePage() {
        return HomePage.class;
    }
 
    private class MyOwnStreamLocator extends ResourceStreamLocator {
        public IResourceStream locate(Class<!--?--> clazz, String path) {
            String extension = path.substring(path.lastIndexOf('.') + 1);
            String simpleFileName = Strings.lastPathComponent(clazz.getName(), '.');
            // "/" is webapp dir
            String location = "/" + simpleFileName + "." + extension;
 
            try {
                // try to load the resource from the web context
                URL url = WebApplication.get().getServletContext().getResource(location);
                if (url != null) {
                    return new UrlResourceStream(url);
                }
            }
            catch (MalformedURLException e) {
                throw new WicketRuntimeException(e);
            }
            // resource not found; fall back on class loading
            return super.locate(clazz, path);
        }
    }
 
}

O WicketApplication.java agora está configurado para usar UTF-8 como padrão e procurar os arquivos HTML dentro de src/main/webapp.

Brincando com Páginas

Uma página geralmente tem um contrutor que recebe os parâmetros via GET agrupados numa classe PageParameters similar à um Map. É no construtor que são adicionados os componentes na página.

Modifique o arquivo HomePage.java para adicionar um link externo e exibir o nome do autor e seu blog na tela de acordo com os parâmetros passados pela URL.

package net.gustavohenrique;
 
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.WebPage;
 
public class HomePage extends WebPage {
    public HomePage(final PageParameters parameters) {
        String author = parameters.getString("author", "Gustavo Henrique");
        add(new Label("author", author));
 
        String blog = parameters.getString("blog", "http://gustavohenrique.net");
        add(new ExternalLink("blog", blog));
    }
}

Depois altere o arquivo HomePage.html:

<h1>Introducao ao Wicket</h1>
<span>Texto daqui sera substituido</span>
<a>Clique aqui para acessar</a>

O componente Label pode ser convertido para vários elmentos HTML, não apenas um span. O texto no código HTML que estiver dentro de um Label gerado pelo Wicket será ignorado no momento da renderização. Quando se adiciona um componente, é obrigatório usar a propriedade wicket:id="id_do_componente", caso contrário será lançada uma Exception. Em modo de produção, o termo wicket:id some (fica apenas id) e as Exceptions lançadas são exibidas como erro 500.

Lidando com Formulários

Em Wicket quase tudo é componente. Para tirar melhor proveito do framework e evitar repetição de código, é preciso entender como “componentizar” as coisas. Imagine que você tenha 10 formulários e todos eles têm em comum os campos telefone, celular e email. A idéia de componentizar seria:

  1. Criar um form com esses componentes e todos os outros herdariam dele
  2. Criar um componente (Panel) agrupando esses componentes e todos os forms adicionariam um único componente (1 Panel contendo 3 TextFields)
  3. Juntar as duas soluções

Conseguiram entender a idéia de componentização? Abaixo um exemplo sobre o que estou falando:

public class ContactPanel extends Panel {
    public ContactPanel(String id) {
        super(id);
        add(new TextField("phone"));
        add(new TextField("mobile"));
        add(new TextField("email"));
    }
}
 
public class CustomerForm extends Form {
    public CustomerForm() {
        add(new ContactPanel());
    }
}

Continuando nossa brincadeira com o framework… Crie um formulário para entrar com nome, idade, site e e-mail do autor:

package net.gustavohenrique;
 
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
 
public class AuthorForm extends Form {
 
    private RequiredTextField name;
    private TextField site;
    private TextField email;
    private TextField age;
    private Label result;
 
    public AuthorForm(String id) {
        super(id);
        add(name = new RequiredTextField("name", new Model("")));
        add(age = new TextField("age", new Model("")));
        add(site = new TextField("site", new Model("")));
        add(email = new TextField("email", new Model("")));
        add(result = new Label("result", new Model("")));
        add(new FeedbackPanel("errors"));
    }
 
    protected void onSubmit() {
        String message = "Olá " + name.getValue() +
                         ", você tem " + age.getValue() +
                         ", seu site é " + site.getValue() +
                         " e seu e-mail é " + email.getValue();
        addOrReplace(result = new Label("result", new Model(message)));
    }
}

Inclua o form no HTML:

<h1>Introducao ao Wicket</h1>
<span>Texto daqui sera substituido</span>
<a>Clique aqui para acessar</a>
<form method="POST">
	<fieldset>
		<label>Nome</label>
		<input type="text" />
		<label>Age</label>
		<input type="text" />
		<label>Site</label>
		<input type="text" />
		<label>E-mail</label>
		<input type="text" />
		<input type="submit" value="Go" />
	</fieldset>
</form>

A novidade é o uso do RequiredTextField, onSubmit, Model e FeedbackPanel. Wicket possui recursos para validações simples como preenchimento obrigatório, apenas números, número máximo e mínimo de caracteres etc. No caso o RequiredTextField requer preenchimento obrigatório. Mensagens de erro validação podem ser personalizas e definidas em um arquivo de properties a parte. Quando ocorre um erro de validação, é chamado o método onError() em vez do onSubmit(). O FeedbackPanel é um componente que automaticamente exibe as mensagens de erros de validação.

É uma boa prática usar Models Objects para guardar valores em componentes. Model é a classe que implementa IModel que por sua vez é uma interface que abstrai o tipo de dado que o componente recebe como valor. Varia de acordo com a implementação de cada componente. Em componentes Label e TextField, o Model pega o toString() do objeto passado. Já em um ListView é preciso que o objeto implemente uma lista java.util.List.

Conclusão

Wicket é um framework baseado em componentes e seu uso depende muito do tipo de problema a ser resolvido. Nem sempre componentizar uma aplicação web vale a pena e mesmo com outros frameworks que não são exclusivos para componentização, é possível aplicar essa técnica. IMHO, prefiro Wicket do que JSF e Struts2 do que Wicket.