Mostrando postagens com marcador Fundamentos do ZK. Mostrar todas as postagens
Mostrando postagens com marcador Fundamentos do ZK. Mostrar todas as postagens

terça-feira, 1 de março de 2011

Construção de interfaces

Neste artigo vou continuar a falar sobre alguns fundamentais do ZK, como é feita a construção dos componentes, de que forma é possível fazê-lo e como anexar os componentes a uma página e será abordado também o conceito de Desktop e Page.
Mas antes gostaria de pautar alguns conceitos importantes:
  1.  JSON(JavaScript Object Notation) - Um formato leve para intercâmbio de dados computacionais. Outro link.
  2. JSP(Java Server Pages) - Uma tecnologia similar ao ASP da Microsoft ou PHP utilizada para desenvolvimento de aplicações web baseada em Java. Outro link.
Esse artigo foi baseado nos textos do site do ZK:
http://books.zkoss.org/wiki/ZK_Essentials/Introduction_to_ZK/Component_Based_UI http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/UI_Composing/Component-based_UI

Visão Geral

Cada objeto de interface é representado como um componente (Component). Assim, compor componentes trata-se da montagem de componentes. Para alterar a interface basta modificar os estados e a relação dos componentes.

Por exemplo, como mostrado abaixo, declaramos um componente Window, permitindo borda(border = "normal") e definindo sua largura em 250 pixels. Foram adicionados 2 componentes Button à Window.


Os componentes são declarados usando ZUML, em um arquivo ZUL, os quais são analisados por um poderoso parser XML do ZK. Os componentes declarados são criados como POJO(Plain Old Java Objects) na JVM no servidor. Suponha que temos uma página ZUL que descreve uma árvore de componentes como o seguinte:


    <button label="Hello" />
    <button label="Good-bye" />


A página com base ZUL renderiza uma janela com dois botões, como mostrado na imagem abaixo:
A marcação em ZUL é equivalente a seguinte declaração POJO em Java:

Window win = new Window();
    win.setTitle("ZKBrasil");
    win.setBorder("normal");
    win.setWidth("250px");
         
Button helloBtn = new Button();
    helloBtn.setLabel("Hello");
    helloBtn.setParent(win);
         
Button byeBtn = new Button();
    byeBtn.setLabel("Good-bye");
    byeBtn.setParent(win);

Os componentes no servidor são depois traduzidos para instruções (em JSON) necessárias para a criação de widget(objetos JavaScript) e enviado então para o cliente.



Como você pode notar, há duas maneiras de declarar um componente, através de XML ou Java puro:
  1. XML
  2. Os componentes são declaradas em arquivos com a extensão ".zul". Uma página ZUL é interpretada dinamicamente no servidor, podemos imaginar como um JSP capacitado com recursos Ajax. Por exemplo, aqui nós criamos um novo arquivo ajax.zul, e vamos implementar um pequeno exemplo de que a entrada do usuário no Textbox é refletido no label abaixo instantaneamente quando o Textbox perde o foco:
        
        <textbox id="txtbx" onChange="lbl.value = txtbx.value"/>
        <label id="lbl"/>
        
    
    
    A marcação declarada acima renderiza o simples programa abaixo:
  3. Java
  4. Os componentes poderiam ser declarados totalmente em Java também:
    package org.zkoss.zkdemo;
     
    import org.zkoss.zk.ui.Page;
    import org.zkoss.zk.ui.GenericRichlet;
    import org.zkoss.zul.*;
     
    public class TestRichlet extends GenericRichlet {
        //Richlet//
        public void service(Page page) {
            
            final Window win = new Window("ZKBrasil", "normal", false);
            win.setWidth("250px");
     
            Vlayout vl = new Vlayout();
            vl.setParent(win);
            
            final Textbox txtbx = new Textbox();
            txtbx.setParent(vl);
            
            final Label lbl = new Label();
            lbl.setParent(vl);
            
            txtbx.addEventListener("onChange", new EventListener(){
               @Override
               public void onEvent(Event event) throws Exception {
                   lbl.setValue(txtbx.getValue());
               }
           });
            
     
            win.setPage(page);
        }
    }
    
    Mais informações sobre Richlets podem ser encontradas aqui.
Além disso, voce pode misturá-las se quiser.

Estruturação dos Componentes


Como uma estrutura de árvore, um componente tem no máximo um pai, enquanto ele pode ter vários filhos.
Alguns componentes aceitam apenas tipos específicos de componentes como filhos, como por exemplo, o componente Grid aceita apenas os componentes Columns, Rows e Foot.
Outros não permitem nenhum tipo de componente, como por exemplo, o componente Textobox.

Um componente sem nehum pai é chamado de componente raiz (root componente). Múltiplos componentes raízes são permitidos em cada página, embora não seja comum.

Note que, se voce estiver usando ZUML, há uma limitação imposta pelo próprio XML: Exatamente uma raiz no documento é permitida. Para especificar várias raízes, é necessário inserir os componentes utilizando tag zk como raiz a qual é uma tag especial que não cria componentes. Por exemplo:


     <!-- O primeiro componente raiz -->
    <div/> <!-- O segundo componente raiz -->


Componentes Filhos


A maioria das coleções retornado por um componente, como Component.getChildren(), são de componentes "vivos", ou seja, existe algum componente adicionado na página. Significa que voce pode adicionar um componente, remover um componente ou remover todos. Por exemplo, para retirar todas os filhos, voce pode fazê-lo através da seguinte declaração:

comp.getChildren().clear();

A qual é equivalente a:

for (Iterator it = comp.getChildren().iterator(); it.hasNext();) {
    it.next();
    it.remove();

No entanto, isso também significa que o seguinte código não irá funcionar, pois a exceção ConcurrentModificationExceptionserá lançada.

for (Iterator it = comp.getChildren().iterator(); it.hasNext();)
    ((Component)it.next()).detach();

Ordenação


Um exemplo menos sutil: ordenação de componentes filhos. A seguinte declaração falha porque a lista contém todos os componentes "vivos" e um componente será retirado quando nós movermos ele para outro local.

Collections.sort(comp.getChildren());

Mais precisamente, um componente tem no máximo um pai e ele tem apenas um ponto na lista dos filhos. Isso significa que a lista é na verdade um conjunto (nenhum elemento duplicado é permitido). Por outro lado, Collections.sort() não consegue tratar um conjunto corretamente. Assim, temos que copiar a lista para outra lista ou vetor, e ordená-la. Components.sort(List, Comparator) é apenas uma maneira simples de reduzir o trabalho.

Desktop, Página e Componente


Imagine que os componentes são atores em um jogo, então a página é a fase onde os componentes desempenham os seus papéis. Uma página (Page) é um suporte de espaço em uma janela do navegador, onde os componentes ZK podem ser anexados e desanexados. A página não é um componente, não implementa a interface Component, mas é uma coleção de componentes e apenas os componentes ligados a uma página estão disponíveis no cliente. Uma página é criada automaticamente quando usuário requisita um recurso como uma página ZUL.

Um desktop (Desktop) é uma coleção de páginas. Ela representa a janela do navegador (ou um tab ou um frame do navegador). Você pode imaginar que um desktop representa uma requisição HTTP independente.


Um desktop é também o escopo lógico que uma aplicação pode acessar em uma requisição. Cada vez que uma requisição é enviada a partir do cliente, ela é associado ao desktop a que ele pertence. O pedido é passado para DesktopCtrl.service(AuRequest, boolean) e, em seguida encaminhado para ComponentCtrl.service(AuRequest, boolean). Significa também que a aplicação não pode acessar componentes em vários desktops ao mesmo tempo.

Um desktop e uma página são criados automaticamente quando o ZK Loader carrega uma página ZUML ou chama um richlet(Richlet.service(Page)). A segunda página é criada quando o componente Include inclui outra página com o modo defer. Por exemplo, duas páginas serão criadas se o seguinte for visitado.

<!-- Página principal -->

  <include src="another.zul" mode="defer"/> <!-- Cria outra página -->


Repare que se o modo não for especificado (ou seja, o modo instant), o incluide não irá criar uma nova página. Em vez disso, ele anexará todos os componentes criados por another.zul como seus componentes filhos próprios.


  <include src="another.zul"/> <!-- default: mode instant -->


É equivalente a seguinte declaração (exceto div não é um proprietário do espaço, veja abaixo)


  <div>
    <zscript>
      execution.createComponents("another.zul", self, null);
    </zscript>
  </div>


Anexar um componente a uma página


Um componente está disponível no cliente somente se ele está anexado a uma página. Por exemplo, a janela criada a seguir não estará disponível no cliente.

Window win = new Window();
win.appendChild(new Label("foo"));

Um componente é um objeto POJO. Se você não tem qualquer referência a ele, ele vai ser reciclado quando a JVM iniciar o Garbage Collection.

Há duas maneiras de anexar um componente de uma página:
  1. Acrescentá-o como um filho de outro componente que já está conectado a uma página (Component.appendChild(Componente), Component.insertBefore(Componente, Componente), ou Component.setParent(Componente)).
  2. Chamar Component.setPage(Page) para anexá-lo a uma página diretamente. É também a maneira de fazer um componente se tornar um componente raiz.

Uma vez que um componente pode ter no máximo um pai e ser anexado no máximo a uma página, é desanexado automaticamente a partir do pai ou da página anterior quando ele é anexado a outro componente ou página. Por exemplo, b será um filho de win2 e win1 não terá nenhum filho no final.

Window win1 = new Window;
Button b = new Button();
win1.appendChild(b);
win2.appendChild(b); //implica desanexar b de win1

Desanexar um componente a uma página


Para desanexar da página, você poderia chamar comp.setParent(null), se não for um componente raiz, ou comp.setPage(null), se for um componente raiz. Component.detach() é um atalho para desanexar um componente sem saber se ele é um componente raiz.

Invalidar um componente


Quando um componente é anexado a uma página, o componente e todos os seus descendentes serão renderizados. Por outro lado, quando um estado de um componente anexado é alterado, apenas o estado alterado é enviado ao cliente para atualização (para melhor desempenho). Embora seja raro, você poderia chamar Component.invalidate() para forçar o componente e seus descendentes a serem renderizados novamente(O ZK Update Engine irá enfileirar os comandos update e Invalidate, e otimizá-los antes de enviar de volta ao cliente para uma melhor performace).

Existem apenas algumas razões para invalidar um componente:

  1. Se você estiver adicionando vários componentes filhos, pelo menos mais de 20, o desempenho será melhor se você invalidar o componente pai. Embora o resultado da resposta Ajax possa ser grande, o navegador é mais eficiente em substituir uma árvore DOM do que a adicionar elementos DOM.
  2. Se um componente tem um bug que uma atualização não atualiza a árvore DOM corretamente, você pode invalidar o seu componente pai, que geralmente resolve o problema (Qualquer tipo de bug pode ser relatado no forum da comunidade. Se confirmado, ele será resolvido na próxima versão).

Não guarde referências de componentes adicionados a uma Página em campos estáticos


Como descrito acima, um desktop é um escopo lógico que a aplicação pode acessar ao atender a uma requisição. Em outras palavras, a aplicação não pode desanexar um componente de um desktop para outro desktop. Isso normalmente acontece quando é referenciado um componente acidentalmente.

Por exemplo, o código a seguir lançará exceção se ele for carregado várias vezes:

 <!--  Instancia e executa foo.Foo -->

e foo.Foo é definido a seguir:

package foo;
import org.zkoss.zk.ui.*;
import org.zkoss.zul.*;
 
public class Foo implements org.zkoss.zk.ui.util.Composer {
   private static Window main; //Errado! Não guarde em um campo estático
   public void doAfterCompose(Component comp) {
       if (main == null)
           main = new Window();
       comp.appendChild(main);
   }
}

A exceção é parecida com essa:

org.zkoss.zk.ui.UiException: The parent and child must be in the same desktop: 
    org.zkoss.zk.ui.AbstractComponent.checkParentChild(AbstractComponent.java:1057)
    org.zkoss.zk.ui.AbstractComponent.insertBefore(AbstractComponent.java:1074)
    org.zkoss.zul.Window.insertBefore(Window.java:833)
    org.zkoss.zk.ui.AbstractComponent.appendChild(AbstractComponent.java:1232)
    foo.Foo.doAfterCompose(Foo.java:10)

Clonando Componentes


Todos os componentes são clonáveis (java.lang.Cloneable). É simples replicar componentes invocando Component.clone().

main.appendChild(listbox.clone());

Note:
  • É um clone de profundidade. Ou seja, todos componentes filhos e descendentes são clonados, também.
  • O componente retornado por Component.clone() não pertence a nenhuma página e ele não tem um pai também. Você deve anexá-lo manualmente, se necessário.

  • O ID será preservado, se houver. Assim, você não pode anexar o componente retornado para o mesmo espaço de ID, sem modificar o ID, se houver.

Da mesma forma, todos os componentes são serializáveis (java.io.Serializable). Como a clonagem, todos os filhos e descendentes são serializados. Se você serializa um componente e, em seguida, deserializá-lo de volta, o resultado é o mesmo que chamar Component.clone()(Mas a performace de executar Component.clone() é muito maior).

sábado, 4 de dezembro de 2010

Arquitetura do ZK

Neste artigo, irei abordar sobre a arquitetura do ZK. Mas antes disso, gostaria de falar sobre alguns conceitos importantes:
  1.  POJO(Plain Old Java Objects) - São objetos Java que não dependem da herança de interfaces ou classes de frameworks externos. Outro link.
  2. JavaScript - Linguagem de script interpretada pelo borwser.
  3. DOM(Document Object Model) - Convenção criada peloa w3school, independente de plataforma e linguagem, para acessarem e manipularem elementos XML, HTML e XHTML através de objetos.
  4. AJAX(Asynchronous Javascript And XML) - Significa o uso de tecnologias como JavaScript e XML, providenciadas pelo navegador, para tornar as páginas Web mais interativas com os usuários, através de requisições assícronas.
Esse artigo foi baseado no texto do site do ZK: http://books.zkoss.org/wiki/ZK_Developer's_Reference/Architecture_Overview

Será abordado 2 perspectivas sobre o ZK: Aplicação e Componente. A primeira mostra uma visão a nível de aplicação, a segunda mostra uma visão a nível de componente.

Visão geral da arquitetura:

Visão do desenvolvedor da aplicação:

O ZK possui uma arquitetura server-centric(centrada no servidor), isso quer dizer que a aplicação ZK roda no servidor. Ela pode acessar os recursos backend, montar a interface com os componentes, ouvir a atividades do usuário, e então manipular componentes para atualizar a interface. Tudo é feito no lado do servidor. A sincronização dos estados dos componetes entre o browser e o servidor é feito automaticamente pelo ZK, e transparente pela aplicação.

Quando executada no servidor, a aplicação pode acessar a stack completa da tecnologia Java. Atividades do usuário são, incluindo AJAX e Server Push, abstraidas para objetos de eventos. A interface são compostas por componentes tipos POJO. Essa é a abordagem mais produtiva para desenvolver uma moderna aplicação web.

A partir do ZK 5, foi introduzido uma mundaça na arquitetura chamada de Server+Client Fusion. Assim a sua aplicação não para no servidor. É possível melhorar a interatividade de sua aplicação através da adição opicional de funcionalidades do lado do cliente(client-side), como o tratamento de evento pelo lado do cliente, efeitos visuais customizados, e até mesmo compor interfaces sem a codificação no lado do servidor. A versão 5 permite a fusão perfeita do servidor e cliente.Assim, voce pode ter o melhor dos 2 mundos: produtividade e flexibilidade.
Um artigo serã escrito mais tarde abordando o Server+Client Fusion.

Visão do desenvolvedor de componente:

Cada objeto de interface no ZK consiste de um componente e um widget. Um componente é um objeto Java sendo executando no servidor o qual representa um objeto de interface o qual pode ser manipulado por uma aplicação Java. Um componente possui todos os comportamentes de objeto de interface exceto a parte visual. Um widget é um objeto Javascript sendo executado no cliente. Esse objeto representa o objeto de interface o qual interage com o usuário. Por isso, um widget geralmente poussi uma aparencia visual, e manipula eventos ocorridos no cliente.

O relacionamento entre um componete e um widget é um um-pra-um. Mas, se um componente não é anexado a uma página, ele não possuirá um widgt correspondente no cliente. Por outro lado, é permitada a aplicação instanciar widgets no cliente diretamente sem o componente correspondente.

É função do componente sincronizar os estados e distribuir a carga. O ZK Client Engine and update Engine trabalham juntos para propocionar um elegante e robusto canal para simplificar a implementação.

Por exemplo, assuma que voce queira implementar um componente que permite o usuário clicar sobre um elemento DOM para mostrar alguma informação detalhada de um componente. Existe pelo menos 2 abordagens para essa implementação. Primeiro, nós podemos carregar a informação detalhada para o cliente quando o widget é instanciado, e então mostrar o detalhe via código client-side. Alternativamente, nós podemos preferir não carregar a informação do detalhe logo de começo, e então enviar uma requisição de volta ao servidor para preencher o detalhe quando o usuário clicar.

Obviamente, a primeira abordagem requer mais largura de banda para a requisição inicial mas possui uma resposta rápida quando o usuário clicar. Porém, isso é geralmente transparente para o desenvolvedor da apicação, e a implementação do componente pode ser aperfeiçoada conforme a fase de desenvolvimento da aplicação.

Fluxo de Execução do carregamento de uma página:

  1. Quando um usuário digita uma URL ou clica num link no browser, uma requisição é enviada para o servidor web. Se a URL requisitada corresponde ao padrão de URL configurado do ZK(isso pode ser customizado), o ZK loader é então invocado para servir a essa requisiçáo.
  2. O ZK loader carrega a página especificada e interpreta-a para criar os componentes especificos de acordo com a página.
  3. Depois de interpretar a página toda, o ZK loader renderiza o resultado em uma página HTML. A página HTML é então enviada de volta ao browser acompanhada do ZK Client Engine.
  4. O ZK Client Engine renderiza os widgets em elementos DOM e insere os elementos DOM na árvore DOM do borwser para torná-los visíveis ao usuário.
  5. Logo, o ZK Client Engine fica no browser para servir as requisições feitas pelo usuário. Se ele vai para outra página, o fluxo de execução começa novamente. Se ele vai enviar uma requisição AJAX de volta, outro fluxo de execução começa conforme descito no próximo item.
O ZK Client Engine é escrito em JavaScript. Browsers irão fazer cache do Z Client Engine, logo ele é geralmente enviado apenas uma vez no primeiro acesso.

Fluxo de Execução ao servir uma requisição AJAX:

  1. O fluxo de execução é iniciado pelo widget ou pela aplicação; geralmente causado por uma atividade do usuário(ou requisitos da aplicação). Isso é geralmente feito por enviar uma evento do lado do cliente(Event) para um widget (Widget.fire(Event, int)).
  2. O evento é então propagado ao pai do widget, ao pai do pai, e finalmente ao ZK Client Engine(Um widget pode escolher parar essa propagação através de Event.stop(Map)).
  3. Se necessário, o ZK Client Engine envia uma requisição AJAX para o ZK Update engine no servidor( Do ponto de vista do servidor, uma requisição AJAX é apenas outra requisição HTTP).
  4. Ao receber uma requisição AJAX, o ZK Update Engine invoca o ComponentCtrl.service(AuRequest, boolean) para o tratamento da requisição.
  5. A manipulação é com o componente. Mas, geralmente é feita a atualização dos estados, se necessário, e então notifica a aplicação enviando eventos( Events.postEvent(Event)).
  6. Se algum evento for enviado, o ZK Update Engine processará eles um-por-um invocando os events listeners, se houver.
  7. O event listener, provido por uma aplicação, pode escolher atualizar os recursos de backend, atualizar os componentes ou postar outros eventos.
  8. Finalmente, o ZK Update Engine recolhe todas as atualizações de componentes, incluindo mudanças nos estados, attachment e detachment. Otimizá-os, e depois envia de volta uma coleção de comandos de volta ao cliente.
  9. O ZK Client Engine avalia cada um desses comandos para atualizar o widget de acordo com cada um. E, os widgets atualizarão a árvore DOM do browser para torná-los disponíveis para o usuário.

Quando enviar uma requisição AJAX:

Quando o ZK Client Engine recebe um evento propagado pelo lado do cliente(Event), ele decidirá se e quando enviar o mesmo de volta ao servidor para processamento adicional.
  1. Se existe um event listener não adiável(deferrable) registrado no servidor, o pedido é enviado imediatamente. Um event listener não adiável é um event listener (EventListener) que não implementa Deferrable. Por padrão, (sem implementar Deferrable) todo o evento é enviado imediatamente ao servidor quando ele é acionado no cliente.
  2. Se existe um event listener adiável registrado no servidor, a requisição será colocado numa fila no cliente e será enviada quando outro evento for acionado e um event listener não adiável for registrado por ele. Para tornar um listener adiável, voce deve implementar Deferrable e retornar true para isDeferrable().
  3. Se o widget declarar o evento o evento como importante(ComponentCtrl.CE_IMPORTANT), o evento será adicionado na fila para ser enviado depois também.
  4. Se não for nenhum dos casos acima ou o widget não tem nenhum componente corresponde no servidor, o evento é cancelado.
Os eventos adiáveis são usados para melhorar o desempenho, minimizando o tráfego entre o cliente e o servidor. É comumente usado por events listener que mantém estados da aplicação, ao invés de gerar respostas visuais.

Bem galera é essa é uma breve visão da arquitetura do ZK.. No próximo artigo irei falar de mais alguns conceitos importantes.