Apache Wicket – Simplicidade na Criação de Páginas Web Dinâmicas

Propaganda
wicket_
Apache Wicket
Um framework simplificado para
construir páginas Web dinâmicas
Q
ual desenvolvedor não gostaria de utilizar orientação a objetos na Web? Utilizar componentes
reutilizáveis e independentes? Utilizar a pura programação Java e HTML sem fortes vínculos?
O framework Apache Wicket pode nos ajudar
nos questionamentos levantados devido a sua simplicidade e agilidade, e vem ganhando espaço no
mercado para o desenvolvimento de aplicações Web.
A principal diferença na utilização do Apache
Wicket para o desenvolvimento de aplicações Web
com outros frameworks da camada de visão é a utilização de componentes que mantêm seus estados
(statefull) e reagem independentemente de outro
componente na entrada de dados do usuário.
O objetivo deste artigo é demonstrar e estimular os desenvolvedores com a criação de uma página
web utilizando o framework Apache Wicket por meio
de seus próprios componentes que interagem com as
entradas do usuário.
A metodologia para criação da página web, proposta neste artigo, compreende primeiramente o
nivelamento conceitual sobre alguns pontos fundamentais: relativo a componentes, modelos, templates, application e session e posteriormente a
aplicação básica com o Apache Wicket versão 1.5.7 e
Spring Framework 3.0.
Conceitos
O Apache Wicket é um framework orientado a
componentes que contém um suporte eficaz e independente a componentes que seguem um determinado modelo, possibilitando que novas instâncias
sejam “plugadas” no framework com simplicidade.
Trazendo de forma simples a separação de responsabilidades entre as classes Java e o HTML, onde o
HTML é usado para criação da interface visual e o
Java para o controle de estado e modelo.
Os componentes do Apache Wicket se ligam facilmente a classes de modelo, como, por exemplo, a
classe Pessoa, através da utilização de models sem a
/ 40
utilização de qualquer arquivo XML para configurar
esse vínculo. Sendo um framework orientado a componentes e não a ações, possibilita tanto um suporte
eficaz a componentes que seguem um determinado
padrão ou modelo de utilização quanto possibilita
que instâncias sejam “plugadas” no framework. Portanto, tudo no Apache Wicket é componente, sendo
uma classe Java.
Componente
Como pilares da construção de uma página, os
componentes são responsáveis por sua própria apresentação, como, por exemplo, o componente TextField, que já cria automaticamente sua identidade
visual.
Cada instância de um componente deve estar
atrelada unicamente a um único ID. Caso ocorra duplicidade de ID o Apache Wicket lança uma exceção
informando que o componente já está em uso.
UsuarioPage
Cadastrar Panel
Form(“Formulário”)
TextField(“Nome”)
Button(“Salvar”)
Figura 1. Componentes aninhados em uma árvore hierárquica.
Os componentes podem estar associados a templates (marcações), onde os arquivos Java e HTML
devem residir no mesmo pacote e conter a mesma
nomenclatura, como, por exemplo, Page e Panel. Em
componentes que não contêm associação a templates, os arquivos estão localizados dentro da superclasse, como, por exemplo, Label e Form.
Luis Gustavo Santos Fernandez | [email protected]
Formado em Engenharia da Computação pelo Centro Universitário de Brasília. Trabalha com desenvolvimento de software há 6
anos. http://www.futurextending.com.br
O Apache Wicket é um framework orientado a componentes que não
exige a utilização de códigos HTML especiais, focado em reutilização e que possibilita uma programação Web divertida. Essas são as
motivações que levaram Jonathan Locke (autor do Wicket) a criar
esse framework que tem ganhado espaço a cada dia no
mercado de trabalho. O fator diferencial deste framework é a
perfeita sincronia entre o que há de melhor em Tapestry e Echo.
Listagem 1. Classe que representa uma página.
public class Pratica extends WebPage{
public Pratica(){
add(new Label(“rotulo”));
add(new TextField<String>(“campoTexto”));
}
}
Listagem 2. HTML sem utilização de marcações
Modelo
A parte central do framework são as classes de
modelos (Model) responsáveis pelo binding dos POJOs aos componentes do Wicket. Essas classes (Model) são identificadas facilmente, pois todo modelo
implementa a interface IModel.
Wicket
POJO
Usuário
TextField(login)
Wicket, “HTML Puro”.
<html>
<body>
<span>Rotulo</span>
<input type=”text” id=”campoTexto”/>
</body>
</html>
Listagem 3. HTML com utilização de marcações
Wicket, “HTML Modificado”.
<html>
<body>
<span wicket:id=”rotulo”>Rotulo</span>
<input type=”text” wicket:id=”campoTexto”/>
</body>
</html>
Observe que na criação dos campos Label e TextField
deve-se obrigatoriamente colocar o ID do componente correspondente ao markup dentro do HTML.
Figura 2. Associação entre os componentes e o HTML.
Model
+login:String
+senha:String
PasswordTextField
(senha)
Figura 3. Vínculo POJO/Componentes.
Markup/Tags
Os documentos HTML que o Apache Wicket utiliza como template podem conter vários atributos
especiais, denominados markup ou simplesmente
tags. Para se evitar entraves com editores de HTML
devido ao uso de tags específicos do Apache Wicket
deve-se declarar o namespace “xmlns:wicket”.
Listagem 4. Exemplos de utilização de namespace.
<?xml version=”1.0” encoding=”UTF-8”?>
<html xmlns=”http://www.w3.org/1999/xhtml”
xmlns:wicket=”http://wicket.apache.org/dtds.data/wicketxhtml1.4-strict.dtd” >
Ou
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0
Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”
xmlns:wicket=”http://wicket.apache.org/dtds.data/wicketxhtml1.4-strict.dtd” >
41 \
Application
A classe base para aplicações do Apache Wicket,
application fornece todo o suporte para configurar
o projeto. Dentro dessa classe pode-se inicializar o
Spring, definir a página principal do projeto, configurar outras bibliotecas como wiQuery e JQwicket, dentre outras configurações. É facilmente configurável
através do arquivo web.xml
Listagem 5. Configurando o Application para Wicket
1.4.x.
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application
2.3//EN”
“http://java.sun.com/dtd/web-app_2_3.dtd”>
<web-app>
<display-name>Exemplo MundoJ</display-name>
<servlet>
<servlet-name>OlaMundoJ</servlet-name>
<servlet-class>wicket.protocol.http.WicketServlet
</servlet-class>
<init-param>
<param-name>applicationClassName
</param-name>
<param-value>br.com.mundoj.application.
MeuApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>OlaMundoJ</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
<param-name>applicationClassName
</param-name>
<param-value>br.com.mundoj.application.
MeuApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>OlaMundoJ</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Criando um projeto Wicket
Para a criação de um projeto com recurso do Apache Wicket, primeiramente, é necessário que esteja
instalado e configurado a ferramenta Maven versão
2.x ou 3.x. Em seguida, utiliza-se o archetype necessário à criação da estrutura básica (figura 7) fornecida
no próprio site do Apache Wicket.
Assim que executado o archetype, deve-se executar o comando mvn clean package para que as bibliotecas sejam baixadas de seus repositórios para um
repositório local do Maven
Listagem 7. Comando para execução do archetype
para criação do projeto base.
mvn archetype:generate -DarchetypeGroupId=org.apache.
wicket -DarchetypeArtifactId=wicket-archetype-quickstart
-DarchetypeVersion=1.5.7 -DgroupId=br.com.mundoj.wicket
-DartifactId=mundoj
Listagem 6. Configurando o Application para Wicket
1.5.x.
<?xml version=”1.0” encoding=”UTF-8”?>
<web-app xmlns:xsi=”http://www.w3.org/2001/
XMLSchema-instance”
xmlns=”http://java.sun.com/xml/ns/javaee”
xmlns:web=”http://java.sun.com/xml/ns/javaee/
web-app_2_5.xsd”
xsi:schemaLocation=”http://java.sun.com/xml/ns/
javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”
version=”2.5”>
<display-name>MundoJ</display-name>
<filter>
<filter-name>OlaMundoJ</filter-name>
<filter-class>org.apache.wicket.
protocol.http.WicketFilter</filter-class>
<init-param>
Figura 4. Estrutura base gerada através do archetype.
/ 42
Ao utilizar o archetype, é criada a classe Wicke- Listagem 10. Código para inicialização do SpringFratApplication (classe suporte para configurações ge- mework para Wicket 1.4.x na classe WicketApplication.
rais) que é configurada através do arquivo web.xml
@Override
para a inicialização do framework Apache Wicket.
Listagem 8. Configuração do WicketApplication cria-
do através do archetype.
<filter>
<filter-name>wicket.mundoj</filter-name>
<filter-class>org.apache.wicket.
protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName
</param-name>
<param-value>br.com.mundoj.wicket.
WicketApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wicket.mundoj</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
protected void init(){
addComponentInstantiationListener(
new SpringComponentInjector(this));
}
Listagem 11. Código para inicialização do SpringFramework para Wicket 1.5.x na classe WicketApplication.
@Override
public void init() {
getComponentInstantiationListeners().add(
new SpringComponentInjector(this));
}
A anotação SpringBean
Para a anotação ser utilizada na injeção de dependências do Spring Beans, seja um serviço ou um
componente, têm-se como pré-requisito a instância
Configurando o SpringFramework
do aplicativo Wicket definida em uma variável de
Para inserção do SpringFramework ao projeto, é segmento local, ou seja, deve-se criar a instância do
necessário adicionar algumas bibliotecas dentro do WicketApplication dentro do arquivo web.xml.
arquivo pom.xml, em especial a biblioteca wicket-spring, que fornece a injeção de dependência dentro Listagem 12. Exemplo de uso da anotação @Springdo Wicket.
Bean e uma simples classe de serviço.
Listagem 9. Código para inserção das dependências
do SpringFramework e Wicket-Spring.
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-spring</artifactId>
<version>${wicket.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
@SpringBean
private MeuServico meuServico{
@Service
public class MeuServico{
//Metodos
}
}
Criando templates
Na criação da identidade visual de um projeto,
deve-se atentar para a criação dos templates, um documento sem conteúdo, responsável somente pela
apresentação visual (contendo cabeçalho e rodapé,
por exemplo). Deste modo, a atenção dos desenvolvedores fica dirigida somente em construir o conteúdo dinâmico de cada página. O Apache Wicket fornece recursos para se utilizar de forma simplificada os
templates.
Para tornar isso mais compreensível, imagine
que se tenha um template base criado por um Web
Designer, no qual está especificado o cabeçalho, corO próximo passo é configurar o Wicket para re- po e rodapé da sua aplicação, onde se deve somente
conhecer o SpringFramework que deve ser feito na alterar o conteúdo do corpo, dependendo da funcionalidade que se acesse.
classe WicketApplication.
43 \
Listagem 13. HTML de exemplo para a geração do
template base.
<html>
<head>
<!-- links para CSS e JS -->
</head>
<body>
<div id=”corpo”>
</div>
<div id=”rodape”>
</div>
</body>
</html>
Para colocar a marcação do Wicket wicket:child,
modificando o HTML enviado pelo Web Designer,
deve-se substituir o conteúdo de marcação pelo
conteúdo do componente derivado da marcação
wicket:extend, estendendo a marcação da superclasse com o conteúdo.
Listagem 14. HTML modificado para ser utilizado
pela aplicação como template base.
<html xmlns:wicket=”http://wicket.apache.org”>
<head>
<!-- links para CSS e JS -->
</head>
<body>
<div id=”corpo”>
<wicket:child/>
</div>
<div id=”rodape”>
</div>
</body>
</html>
Através da herança “se diz” ao Wicket que as novas páginas utilizarão propriedades da superclasse,
consequentemente a classe filha utilizará a estrutura
de template desenhada.
Listagem 15. Utilização da herança para reutilização
do template.
public class MundoJ extends BasePage{
}
Listagem 16. HTML para o formulário.
//Listagem 16. HTML para o formulário.
<wicket:extend>
<form wicket:id=”form”>
<div>
Nome:
<input type=”text” wicket:id=”nome”/>
</div>
<div>
Sexo:
<span wicket:id=”sexo”/>
</div>
<div>
Estado Civil:
<select wicket:id=” estadoCivil”>
</select>
</div>
<input type=”submit” value=”Salvar”
wicket:id=”botaoSalvar”/>
<input type=”submit” value=”Voltar”
wicket:id=”botaoVoltar”/>
</form>
</wicket:extend>
Cada elemento representado no HTML necessariamente terá que ter um componente associado na
classe Java. A inicialização dos componentes acontece dentro do construtor da classe, é nesse momento
que se adiciona os componentes. Para se estabelecer
o vínculo do POJO (necessário implementar a interface java.io.Serializable) aos componentes, deve-se
utilizar uma classe model, no caso deste artigo a classe CompoundPropertyModel.
Listagem 17. POJO Pessoa.
//Imports
public class Pessoa implements Serializable{
private String nome;
private Date dtNascimento;
private Sexo sexo;
private EstadoCivil estadoCivil;
//getters and setters
}
O formulário contém os componentes como campo texto, rádio e combobox, o qual devemos adicioná-los dentro do nosso formulário.
Criando formulário
Simulando um cadastro
Para simular um cadastro, supondo que o template base já tenha sido criado para a aplicação, deixando que somente as páginas de formulários com
seus devidos campos sejam construídos de acordo
com as funcionalidades, basta ter o HTML contendo
as marcações do Apache Wicket e a classe Java correspondente.
/ 44
Os componentes do formulário, como campo texto, rádio e combobox, devem ser adicionados dentro
do formulário. Basta adicionar o componente org.
apache.wicket.markup.html.form.Form dentro da
página (um componente WebPage para o Apache Wicket).
O formulário tem a responsabilidade de manipular um POJO, correspondente ao seu caso de uso,
e para isso utiliza-se um tipo genérico para “tipar”
o formulário. Além da tipagem, deve-se vincular o
Para esses componentes, o Wicket, por padrão,
POJO ao seu model utilizando a classe org.apache. utiliza o método toString() para renderizar os valowicket.model .CompoundPropertyModel, responsá- res do rótulo de cada opção e sua posição dentro da
vel por realizar o “binding do POJO ao HTML”.
lista de valores. Para personalizarmos a renderização
desses componentes, pode-se utilizar a classe org.
Listagem 18. Criando o formulário para o POJO
apache.wicket.markup.html.form.IChoiceRenderer.
Pessoa.
Esse componente separa os valores do “id” e do “displayValue” dos componentes para renderizar as inForm<Pessoa> formulario = new Form<Pessoa>(“form”);
formações personalizadas.
formulario.setModel(new CompoundPropertyModel<Pess
oa>(p));
add(formulario);
Adicionando um campo-texto
A adição do campo-texto ao formulário é bem
simples, basta criar uma instância do objeto org.apache.wicket.markup.html.form.TextField e colocar o
ID correspondente no HTML.
Listagem 19. Adicionando o campo-texto ao formulário.
formulario.add(new TextField<String>(“nome”));
Adicionando um campo de rádio e
combobox
Listagem 22. Adicionando o campo combobox ao
formulário e utilizando o ChoiceRenderer.
//A classe EstadoCivil utilizada é um enum
IChoiceRenderer<EstadoCivil> rendererCombo =
new ChoiceRenderer<EstadoCivil>
(“descricao”, “sigla”);
DropDownChoice<EstadoCivil> combo =
new DropDownChoice<EstadoCivil>
(“estadoCivil”, Arrays.asList(EstadoCivil.values()),
rendererCombo);
formulario.add(combo);
Para cada elemento contido na lista criada através do enum EstadoCivil será criado um novo elemento visual, no caso de DropDownChoice será criado um novo “option”. Listagem 23. HTML gerado pela renderização dos
Para os campos de escolha, como o campo rádio elementos do DropDownChoice.
ou combobox, é necessário criar uma lista de objetos
referente àquele campo. Essa lista pode ser estática <select id=”estadoCivil” name=”estadoCivil”>
<option selected=”selected”
ou um retorno de uma consulta ao banco de dados. A
value=””>Selecione</option>
vinculação do componente através do ID correspon<option value=”C”>Casado</option>
dente no HTML é imprescindível.
<option value=”S”>Solteiro</option>
Listagem 20. Adicionando o campo rádio ao formulário.
//A classe Sexo utilizada é um enum.
List<Sexo> listaSexo = Arrays.asList(Sexo.values());
RadioChoice<Sexo> radioChoice = new
RadioChoice<Sexo>(“sexo”, listaSexo);
formulario.add(radioChoice);
<option value=”55”>Viúvo</option>
<option value=”18”>Divorciado</option>
</select>
Esses componentes oferecem a possibilidade de
reprogramarmos o componente e substituir o método wantOnSelectionChangedNotifications, forçando
os ids e a volta de valores para o servidor a cada mudança de valores.
Para cada elemento contido na lista de objetos,
criada através do enum Sexo, será renderizado um Adicionando o botão de submit
Para conclusão do cadastro, é necessário um
novo elemento visual, ou seja, um novo “input” do
botão para submeter os dados inseridos pelo usuátipo “radio”.
rio que, neste artigo, se utiliza do componente org.
Listagem 21. HTML gerado pela renderização dos
apache.wicket.markup.html.form.Button.
Deve-se
elementos do Radio.
observar que, dentro de um formulário, pode haver
diferentes componentes de botão com diferentes
<span>
comportamentos.
<input name=”sexo” type=”radio” checked=”checked”
A propriedade de modelo para o botão é o “vavalue=”M” id=”id90-M”/>
<label for=”id90-M”>Masculino</label>
lue”, e esse atributo servirá de rótulo para o botão.
<input name=”sexo” type=”radio” value=”F”
Quando se submeter o formulário por padrão (click
id=”id90-F”/>
no botão), o método onSubmit() é invocado primei<label for=”id90-F”>Feminino</label>
ramente e logo em seguida o método onSubmit() do
</span>
formulário.
45 \
Caso o comportamento do botão seja de “Voltar” Listagem 27. Enum Sexo e Estado Civil.
pode-se utilizar a propriedade defaultFormProcessing definindo-o como false fazendo com que as validações de formulários sejam ignoradas.
public enum Sexo {
Listagem 24. Criando o botão voltar ignorando a
validação do formulário.
Button botaoVoltar = new Button(“botaoVoltar”);
botaoVoltar.setDefaultFormProcessing(false);
formulario.add(botaoVoltar);
}
public enum EstadoCivil {
SOLTEIRO(“SO”, “Solteiro”),
CASADO(“CS”, “Casado”),
DIVORCIADO(“DV”, “Divorciado”),
VIUVO(“VV”, “Viuvo(a)”);
private String sigla;
private String descricao;
Para a navegação entre as páginas se utilizará o
método setResponsePage que define qual página irá
responder ao request.
private EstadoCivil(String sigla, String descricao){
this.sigla = sigla;
this.descricao = descricao;
}
Listagem 25. Criando o botão salvar e reimplementando o método onSubmit().
public String getSigla() {
return sigla;
}
Button botaoSalvar = new Button(“botaoSalvar”){
//Re-implementando o metodo onSubmit
@Override
public void onSubmit() {
//lógica para persistência dos dados
//Retorno de página exemplo 1
setResponsePage(PaginaIndex.class);
//Retorno de página exemplo 2
setResponsePage(new PainaIndex());
//Retorno de página exemplo 3
setResponsePage(webPage);
}
};
formulario.add(botaoSalvar);
M, F;
}
public String getDescricao() {
return descricao;
}
Listagem 28. BasePage.java
//A funcionalidade dessa classe é ser base para o
// template da aplicação
public class BasePage extends WebPage{
}
Versão final do HTML e das classes Java
Listagem 26. WicketApplication.java
public class WicketApplication extends WebApplication {
@Override
public Class<MundoJ> getHomePage() {
//Página de inicialização da aplicação
return MundoJ.class;
}
@Override
public void init() {
//Inicializando o Spring para Wicket 1.5
getComponentInstantiationListeners().add(
new SpringComponentInjector(this));
}
}
/ 46
Listagem 29. BasePage.html
<html xmlns:wicket=”http://wicket.apache.org”>
<head>
<!-- links para CSS e JS -->
</head>
<body>
<div id=”corpo”>
<wicket:child/>
</div>
<div id=”rodape”>
</div>
</body>
</html>
Listagem 30. MundoJ.java
public class MundoJ extends BasePage{
public MundoJ(){
Pessoa p = new Pessoa();
Form<Pessoa> formulario =
new Form<Pessoa>(“form”);
formulario.setModel(
new CompoundPropertyModel<Pessoa>(p));
formulario.add(new TextField<String>(“nome”));
List<Sexo> listaSexo = Arrays.asList(Sexo.values());
RadioChoice<Sexo> radioChoice =
new RadioChoice<Sexo>(“sexo”, listaSexo);
formulario.add(radioChoice);
IChoiceRenderer<EstadoCivil> rendererCombo =
new ChoiceRenderer<EstadoCivil>(“descricao”,
“sigla”);
DropDownChoice<EstadoCivil> combo =
new DropDownChoice<EstadoCivil>(“estadoCivil”,
Arrays.asList(EstadoCivil.values()),
rendererCombo);
formulario.add(combo);
Button botaoVoltar = new Button(“botaoVoltar”);
botaoVoltar.setDefaultFormProcessing(false);
formulario.add(botaoVoltar);
Button botaoSalvar = new Button(“botaoSalvar”){
//Re-implementando o metodo onSubmit
@Override
public void onSubmit() {
setResponsePage(Home.class);
};
formulario.add(botaoSalvar);
}
}
add(formulario);
Estado Civil:
<select wicket:id=”estadoCivil”>
</select>
</div>
<input type=”submit” value=”Salvar”
wicket:id=”botaoSalvar”/>
<input type=”submit” value=”Voltar”
wicket:id=”botaoVoltar”/>
</form>
</wicket:extend>
Considerações finais
O artigo demonstra como é simples e prática a
utilização do framework Apache Wicket, que vem
cada vez mais ganhando espaço no mercado, onde
temos a separação dos códigos dinâmicos e códigos
estáticos (Java e HTML + JavaScript).
A utilização desse framework permite aos designers a liberdade de criar a identidade visual de um
sistema, sem que o mesmo se preocupe com qual
componente o desenvolvedor vai utilizar e que poderá afetar sua criação.
O Apache Wicket é um framework da camada de
apresentação, cuja responsabilidade é manipular o
request e response. A simplicidade na sua utilização
está em acoplar outros frameworks para serem utilizados como serviços ou persistências de dados, utilizando o Spring Framework ou Guice para injeção de
dependências e JPA/Hibernate para a persistência de
dados (exemplos).
A utilização do Apache Wicket pode ser resumida
em uma palavra: “simplicidade”.
}
/referências
Listagem 31. MundoJ.html
<wicket:extend>
<form wicket:id=”form”>
<div>
Nome:
<input type=”text” wicket:id=”nome”/>
</div>
<div>
Sexo:
<span wicket:id=”sexo”/>
</div>
<div>
> Apache Wicket: http://wicket.apache.org/
> Migrando versão 1.4.x para 1.5.x: https://cwiki.apache.
org/WICKET/migration-to-wicket-15.html
> Exemplos práticos: http://www.wicket-library.com/
wicket-examples/ajax/
> Reference library: https://cwiki.apache.org/WICKET/
reference-library.html
> Spring Framework: http://www.springsource.org/
> Wicket-Spring: https://cwiki.apache.org/WICKET/spring.
html
47 \
Download