Programador ajudante e aprendiz da comunidade open source.

Criando Template com Sitemesh

É comum trabalharmos com includes em nossas páginas para evitarmos a repetição de códigos que não serão alterados. Essa com certeza é uma ótima solução, mas já pensou se tivéssemos 100 páginas? Todas elas precisariam de includes, estes em sua maioria iguais. Então por que não automatizar isso?

Para essa tarefa temos alguns web-page layouts como o Tiles famoso no mundo Struts, o Facelets do mundo JSF e o Sitemesh, que se destaca por ser simples e poderoso.

Objetivo:

Criar alguns templates (decorators) com diferentes configurações utilizando o Sitemesh para automatizar a inclusão (decoração) de nossas páginas.

Para servir de decorator podemos aproveitar o layout do artigo "Criando Layout com CSS", assim veremos como aplicar o Sitemesh e seus benefícios.

Criando o esboço dos templates:

Criaremos 2 (dois) diferentes decorators, principal.jsp e usuario.jsp:


Template Principal

O decorator principal.jsp composto por quase todos os número, menos o 4 que será o conteúdo dinâmico, será o decorator default do sistema, que irá decorar as páginas requisitadas.


Template Usuário

O decorator usuario.jsp com o container número 3 sendo a parte dinâmica, será um decorator alternativo, no qual teremos opção de incluí-lo no lugar do default.

Para comerçarmos as configurações vamos baixar a última versão do Sitemesh no site oficial e adicionar a lib sitemesh-2.x.x.jar na pasta WEB-INF/lib do nosso projeto.

Ativando o Sitemesh (web.xml):

<filter>
  <filter-name>sitemesh</filter-name>
  <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>sitemesh</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

No nosso arquivo web.xml indicamos qual é o filtro responsável por decorar nossas páginas requisitadas e qual o padrão de URL que será interceptada. Repare que poderíamos colocar /*.jsp, porém estaríamos amarrados a este padrão, então deixamos interceptar tudo a princípio e refinamos isso mais tarde.

Flexibilidade de configuração:

O Sitemesh não nos obrigam a especificar um caminho completo, até a estensão do arquivo, isso nos da uma maior flexibilidade, pois podemos fazer regras baseado em apenas uma parte da URI. Pra quem trabalha com frameworks que desfrutam de URIs amigáveis como o VRaptor, pode fazer regras no seguinte estilo:

Configurando os decorators (decorators.xml):

Este arquivo que também se encontra dentro da pasta WEB-INF manterá toda nossa lógica de decoração.

<decorators defaultdir="/decorators">
  <decorator name="principal" page="principal.jsp">
    <pattern>/*</pattern>
  </decorator>

  <decorator name="usuario" page="usuario.jsp">
    <pattern>/usuario.jsp</pattern>
  </decorator>
</decorators>
  • Primeiro indico o diretório onde se encontra meus decorators através do atributo defaultdir. Isso evita a repetição desse caminho;
  • Depois coloco minha primeira regra de decoração dizendo que qualquer tipo de URL acionada terá seu conteúdo decorado por principal.jsp. Também dou um nome para esta regra, isso é necessário;
  • Outra regra especializada falando que se a página usuario.jsp for chamada, esta será decorada pelo decorator usuario.jsp.

Bem, nossa configuração esta pronta, mas precisamos agora criar os decorators.
De acordo com o exemplo do artigo "Criando Layout com CSS" a única coisa que irá mudar será o conteúdo dinâmico, pois agora não podemos simplesmente colocar em tal container um include fixo desta forma:

<div id="sub-conteudo"><%@ include file="/index.jsp" %></div>

Sem utilizar uma solução mais primitiva que passei nesse comentário, o Sitemesh nos possibilita a inclusão do conteúdo requisitado diretamente da seguinte forma:

<div id="sub-conteudo"><decorator:body/></div>

Isso faz com que o body da página requisitada seja capturado e incluído em nosso decorator, e em vez de ser renderizada a página requisitada, é renderizado o decorator com este conteúdo incluso, e esta é a mágica.

Temos outras tags de inclusão mais utilisadas como:

  • <decorator:title default="Título padrão"/>: Captura o título da página interceptada; e
  • <decorator:head/>: Captura o cabeçalho.

Você pode deixar suas páginas sem os conteúdo <head/>, e o colocando diretamente no decorator. Se sua página não tiver nem mesmo a tag <body/> toda a página será considerada body.

Trabalhando com excludes:

Também podemos criar regras para evitar a decoração, sendo útil principalmente para conteúdos requisitados via ajax nos formatos JSON ou XML, já que ambos não podem conter nada mais além do que seus dados bem formados.

<excludes>
  <pattern>/sobre.jsp</pattern>
  <pattern>/*/exibir*</pattern>
  <pattern>/*?exclude*</pattern>
</excludes>

Desta forma sempre que for chamado a página sobre.jsp nenhuma decoração será acionada.
Podemos utilizar o caracter * (asterisco) para indicar qualquer coisa em seu lugar e criar um padrão de exclusão.
Podemos também utilizar os nomes de parâmetros para indicarmos uma exclusão, no caso optei por sempre evitar a decoração quando houver o parâmetro chamado exclude, desta forma evito de escrever todas as combinações de URIs que quero excluir, deixando essa decisão ser feita diretamente em minhas páginas.

Chamadas com retorno em formato JSON ou XML sempre são excluídas, então um parâmetro ajuda muito, pois precisamos apenas adicioná-lo para capturarmos o conteúdo puro sem o código adicional dos decorators.

No site do Sitemesh você encontra algumas outas configurações, mas posso te garantir que para a maioria das aplicações você não precisará mais do que isso. (:

Link do projeto:

http://github.com/wbotelhos/criando-template-com-sitemesh

  1. ESPEDITO LEAL 13 Jun 2015 22:55

    Boa noite Washington!

    Estou usando o Sitemesh com o Vraptor 4. Aprendi a usar o Sitemesh através das suas dicas. Depois de muito pesquisar e não obter sucesso resolvi postar aqui. Estou o seguinte problema:
    Tenho uma nav bar usando Bootstrap com Vraptor. Todos os links da nav bar funciona normalmente, porém, quando uma view na sua url precisa chamar parâmentros na url a lista de opções do menu é desabilitada.

    Não tenho certeza se o problema é com o Sitemesh ou com o controler do Vraptor.

    Valeu!

  2. fabio 6 Fev 2013 10:34

    Parabens!!!
    seguinte, minha duvida e porque usar o sitemesh, sendo que a primeira impressao foi que ele substituiria o <%@ include %>
    não saquei isso ???? a magica!!!!!

  3. Marcelo Yukio 29 Jun 2012 12:13

    Fala WB! Bacana o site... (Y)

    Cara, segui seu exemplo so sitemesh...
    até aqui beleza...

    Precisei usar IFRAMES usando o thickbox, usei esse exemplo: http://www.christianschenk.org/projects/wordpress-thickbox-plugin/

    A pergunta é: Sitemesh funciona no IFRAME ?

    Valeu !

    1. Washington Botelho autor 4 Jul 2012 23:04

      Oi Marcelo,

      Se o conteúdo do iframe é uma outra requisição deveria funcionar sim.

  4. Fernando Falci 21 Jun 2012 07:26

    nbprojectbuild-impl.xml:710: O módulo não foi implementado.

    Recebo essa mensagem de erro :S
    Isso aconteceu quando fiz a alteração no web.xml

    Depois de varias tentaivas e relidas no post, sem sucesso, baixei o projeto do github e mesmo assim não rodou, mesmo erro.

    1. Washington Botelho autor 25 Jun 2012 20:20

      Oi Fernando Falci,

      Me parece problema de configuração do NetBeans e não do Sitemesh.
      Difícil dar o mesmo erro, já que esta claramente referenciando O NetBeans e o meu projeto é do Eclipse: nbproject

  5. cesar 31 Mai 2012 05:12

    Olá, pra chamada ajax de uma página ( em q a URL nao muda) como faço para que a página chamada não esteja decorada? ja pesquisei e muito,...e nada..

    1. Washington Botelho autor 31 Mai 2012 11:18

      Oi César,

      Esta explicado na parte Trabalhando com excludes:
      Passa algum parâmetro na requisição para indicar a requisição ajax.

      1. cesarh 8 Jun 2012 11:15

        WB, como configurar decorator quando se faz requisição via ajax. No caso a url nao muda, o template aparece todo um dentro do outro. como excluir decoração nas paginas internas ?

        obrigado!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        1. Washington Botelho autor 25 Jun 2012 20:14

          Oi cesarh,

          Passa um indicador na url e exclui este.
          Algo legal de se fazer é seguir a idéia do Rails e usar .json no final das URIs que retornam JSON.

  6. Victor Hugo 24 Mai 2012 08:06

    Tentei fazer uma modelo para o meu sistemas Web com vRaptor, mas acho que não deu muito certo.
    A impressão que eu tive é que ao usar :
    no decorator principal.jsp
    parece que nas outras páginas não reconhece a tag...

    Por exemplo no formulario de login, action="" , dá algum bug, que não identifica a url.

    Sendo que ao retirar as configurações do sitemesh funciona normalmente...

    Tem algo a ver isso?

    1. Washington Botelho autor 25 Mai 2012 07:01

      Oi Victor,

      O seu código não apareceu. Utilize a sintaxe do Markdown para escrevê-lo.
      E dá uma olhada no projeto exemplo.

  7. SuporteDev 21 Mar 2012 22:11

    Ele funciona com XHTML? eu fiz um teste com XHTML e não rolou, ele não da erro nenhum porém a página vem em branco.

    1. Washington Botelho autor 23 Mar 2012 12:15

      Oi SuporteDev,

      Funciona sim, se o que você dizer ser XHTML são apenas ter tags especiais no seu código.
      Se esta vindo em branco, talvez o seu filtro para mapear as URIs não esta batendo com o que você esta acessando, sendo assim não irá trazer nada.

  8. Rodrigo 1 Out 2011 14:24

    Parece que o sitemesh morreu, não há updates desde fevereiro.
    Alguma sugestão? Usar o a versão antiga? outro framework semelhante?

    1. Washington Botelho autor 8 Out 2011 18:22

      Oi Rodrigo,

      Estão criando a versão nova do Sitemesh sim, a versão 3: http://www.sitemesh.org.

      Existem alernativas como o FreeMaker, mas pessoalmente gosto do Sitemesh e enquando ele atender minhas necessidades que é simplesmente decorar estará de ótimo tamanho. (:

      Você encarou algum problema? Precisou de algo além?

      1. Rodrigo 30 Out 2011 07:28

        Não, a dúvida era só qual versão usar em um projeto iniciando, mas estou usando a versão 2 mesmo. Valeu!

  9. Washington Machado 22 Abr 2011 21:35

    Estou precisando de uma dica... o decorator funciona aqui para qualquer página do diretório web, acredito que equivalente ao WebContent do eclipse (estou usando Netbeans).

    Mas esse mesmo projeto usa Vraptor e não consigo que o decorator renderize para as páginas que estão em `web/WEB-INF/jsp/*, tem alguma dica de como posso arrumar?

    Obs.: no meu projeto uso a versão 2.4.1 do sitemesh que retirei do seu exemplo.

    Notei que a página de download do sitemesh está fora do ar e a versão 3.0-alpha2 está disponível em sitemesh.org. Você chegou a testar esse beta? Se sim, o que achou?

    Obrigado.

    1. Washington Botelho autor 23 Abr 2011 15:46

      Oi Washington (xará),

      Nunca usei o VRaptor no Netbeans junto com o Sitemesh, mas deveria decorar, já que o Sitemesh pega o resultado em tempo de execução. Verifica se o mapeamento do Sitemesh esta escrito primeiro que o do VRaptor no web.xml.

      Acabei de testar a versão 3.0-alpha2 e me parece que ela gera uma exception com o provider de transação, que é um interceptor, da JPA:

      java.lang.IllegalStateException: EntityManager is closed

      Achei a declaração de tag mais burocrática;
      O XML é mais poderoso em suas configurações, além de ser possível configurar via Java;
      É legal não precisar mais de importar a taglib, apesar do Eclipse ficar apresentando um warning de unknow;
      Gostei do novo nome sitemesh3.xml, mas poderiam tirar o número da versão pra ficar mais genérico.

      Acho que vou ficar na versão 2 até que a 3 esteja estável. (:

      1. Washington Machado 23 Abr 2011 17:21

        Realmente era só inverter a declaração do filtro no xml... já que funcionou vou ficar na versão 2 por ora.

        Há alguma explicação pra termos que inverter essa declaração do filtro?

        Obrigado pela ajuda.

        []s

        1. Washington Botelho autor 23 Abr 2011 17:36

          Oi Washington,

          O filtro do Sitemesh deve vir primeiro, pois se o VRaptor capturar a requisição o Sitemesh não terá mais oportunidade de decorar, pois ele não reconhece o dispatcher do VRaptor.

          1. Washington Machado 23 Abr 2011 18:00

            Maravilha, agora já estou livre dos include...

            Obrigado novamente.

  10. Dev.softhouse 31 Mar 2011 08:12

    Bom dia Botelhos tudo bem?

    To olhando seus posts e vi que vc tem bastante coisa da camada de apresentação, to fazendo uma sistema usando VRaptor e as regras de negócio e a estrutura do programa está saindo, mas com relação a camada de apresentação to vendo que ela está muito pobre, gostaria de saber se posso empregar essas técnicas no meu projeto (se são compatíveis com o VRaptor) e também quais vc recomenda.
    Eu queria chamar um Form de dentro de outro form como acontece em aplicações desktop onde temos que fazer isso para buscar um item que depende de outro no banco de dados.

    1. Washington Botelho autor 2 Abr 2011 11:08

      Oi Dev,

      O VRaptor é a parte Java, ou seja, o backend da sua aplicação, logo ele é totalmente compatível, pois são duas coisas diferentes.

      Eu gosto de usar o jQuery UI junto com layouts criados com CSS mesmo. Ele já vem com estilos de barra 3D, janelas modais, ícones, barra de loading, efeitos e tudo mais que se fóssemos criar na mão daria mais trabalho.

      Para buscar estes dados você já pode listá-los em uma combobox restringindo o tamanho da mesma para o usuário fazer um scroll. Ou você pode fazer um campo de consulta usando autocomplete.

      No www.moviecollection.com.br eu busco os artistas e diretores para adicioná-los no filme dessa forma e não sobrecarrega o servidor, já que pode ser configurado para a busca ser feita só a partir de tantos caracteres digitados e com um intervalo de tempo para não haver várias requisições.

  11. Felipe 15 Nov 2010 18:20

    Boa noite,
    Tenho um projeto o qual uso dois decoradores
    o principal.jsp que irá decorar todas as urls /*
    e outro que irá decorar apenas jsps espedificos
    que estão dentro de uma pasta no contexto do meu projeto, porém ao configurar este segundo decorador com a pattern pasta* ,
    ele não renderiza como devia ,
    se alguem puder ajudar agradeço...

    1. Washington Botelho autor 16 Nov 2010 21:22

      Oi Felipe,

      Qual URI exatamente você tentou acessar?
      Como esta o mapeamento do seu decorators.xml?

      Vou deduzir que utilizou literalmente, "pasta*", e o restante esta tudo correto.
      Então o problema pode ser a barra invertida. tenta mapear da seguinte forma:

      <decorator name="secundario page="secundario.jsp">
        <pattern>/pasta/*</pattern> <!-- barra normal -->
      </decorator>
      
  12. Henrique 4 Nov 2010 17:22

    Olá cara!

    vlw por responder!

    mais surgiu outra dúvida aqui!

    então se eu quiser que o javascript funcione apenas em uma pagina específica que eu carregar , o javascript é declarado em cada jsp, ou só posso declarar no frame.jsp?

    1. Washington Botelho autor 4 Nov 2010 17:52

      Henrique,

      Neste caso você deve escrever ou importar o JS diretamente em cada página específica.
      Se você colocar o script no decorator, todos as outras páginas irão compartilhá-lo.

  13. Henrique 29 Out 2010 17:48

    Cara estou tentando por um javascript para fazer uma máscara no campo de data de um formulário, mais não tá funcionando com o sitemesh, só contigo fazer funcionar, com um arquivo html fora do projeto. Existe alguma configuração para o sitemesh aceitar esse tipo de código?

    Parabéns pelo site!

    1. Washington Botelho autor 29 Out 2010 18:34

      Oi Henrique,

      O javascript funciona normalmente no decorator do Sitemesh.
      Inclusive a cada refresh o decorator é recarregado e o javascript é executado novamente.
      Verifique se seu script esta dentro de uma função ready do jQuery para que ele seja executado somente após a página estar carregada, já que para aplicar a máscara o elemento HTML deve estar carregado:

      <script type="text/javascript">
        $(function() {
        // Seu código que necessita do elemento existir para ser executado corretamente.
        });
      </script>
      
  14. Felipe Viana 19 Out 2010 13:07

    Obrigado pela informação e mais uma vez Parabens
    pelo artigo e por todo o site.

    Abraço

  15. Felipe Viana 19 Out 2010 12:31

    Obrigado , ja consegui configurar, o meu erro era que
    tinha esquecido de colocar a taglib para o meu principal.jsp.

    Podes me explicar para que serve ela ?
    e por que o prefix="decorator"?

    Grato

    Obrigado

    1. Washington Botelho autor 19 Out 2010 12:39

      Oi Felipe,

      A taglib é o import da biblioteca do Sitemesh. Sem ela você não consegue chamar as funcionalidades do Sitemesh.
      O atributo prefix é o nome pelo qual você irá invocar essas funcionalidades.
      Pode ser qualquer nome, mas por convenção decorator é o nome utilizado, assim como utilizar c para a JSTL Core, fmt para a Format, fn para a Functions etc.

  16. Felipe Viana 18 Out 2010 13:49

    Parabens pelo Artigo...

    Sou iniciante em Java para Web e estou com dificuldades para configuração do ambiente elcipse com o Sitemesh.
    Se puder ajudar ficarei grato.
    Tenho que instalar algum plugin ?
    Estou com o Eclipse Ganymede

    Grato Felipe Viana

    1. Washington Botelho autor 18 Out 2010 13:59

      Oi Felipe,

      Você não precisa de instalar nenhum plugin.
      Basta colocar a lib do Sitemesh na pasta lib, mapear o filtro dele no web.xml e criar o decorator.xml para criar as regras.
      Tudo que é necessário para que ele funcione esta descrito no artigo.
      Qual exatamente é o seu problema?

  17. Washington Botelho 1 Out 2010 17:42

    Oi Facundo,

    Nunca testei o Sitemesh no Struts para ver se de fato se faz necessário o PageDecoraterMapper.
    Eu prefiro deixar as configurações centralizadas no decorators.xml e evitar criar o sitemesh.xml e sair distribuindo meta tag. Mas se dessa forma resolveu, que bom! Fica a dica ai para futuros problemas com o Struts. :)

    Muito obrigado pelo feedback.
    Abraço.

    1. Washington Botelho autor 1 Out 2010 17:42

      Oi Facundo,

      Nunca testei o Sitemesh no Struts para ver se de fato se faz necessário o PageDecoraterMapper.
      Eu prefiro deixar as configurações centralizadas no decorators.xml e evitar criar o sitemesh.xml e sair distribuindo meta tag. Mas se dessa forma resolveu, que bom! Fica a dica ai para futuros problemas com o Struts. :)

      Muito obrigado pelo feedback.
      Abraço.

  18. Facundo 1 Out 2010 13:31

    Muito obrigado amigo, desculpa a demora, aconteceu que tive que deijar aquele projeto para fazer um outro mais urgente. Aquele problema eu solucionei de um outro jeito. Com PageDecoraterMapper nessa explicação http://tim.oreilly.com/pub/a/onjava/2004/09/22/sitemesh.html?page=3 (adicionando un attributo no META tag que diz a que decorator correponde a pagina ) Ainda assim o que vocei falou me ajudo com outro problema que tive ahora com uma pagina na cual estava usando ajax . Agreguei aquele parametro em cada link no href gerado por javascrypt e funcionou. Obrigado amigo,

  19. Facundo 24 Ago 2010 14:27

    " Outra cosa , dixei o "jsp:include" por que o "decorator:body" no esttava carregando nada, não ta faltando alguma coisa, como carregar os tags do sitemesh nos decorators alguma coisa assim?

  20. Facundo 24 Ago 2010 14:26

    OI W, muito bom suas dicas, sou iniciante e meu idioma é o espanhol assim que disculpe meu portugês se esta errado. O problema com este método no meu caso é que no topofilme no layout "principal" eu coloquei um FormBean do struts para o acceso, um a vez validado pasaria ao layout de usuario, mas eu tenho que colocar que exclua *.do, se não no permite o foward do struts, fica no principal. Fazendo isto,ainda assim não consigo carregar o layout de usuario no caso de validacion exitosa. Outra cosa , dixei o por que o no taba carregando nada não ta faltando alguma coisa, como carregar os tags do sitemesh nos decorators alguma coisa assim?

    1. Washington Botelho autor 26 Ago 2010 14:10

      Oi @Facundo,

      Seu problema possa estar na URL que você esta usando para fazer sua action e seu forward.
      Quando você precisar de fazer uma action, inclua um parâmetro que esteja mapeado como exclude, na URL para evitar que o Sitemesh intercepte esta ação.
      Você pode usar por exemplo:

      <excludes>
        <pattern>/*?exclude*</pattern>
      </excludes>
      

      Então você não precisará fazer *.do e generalizar sua exclusão, fazendo esta exclusão manualmente quando necessário.

      Verifique se a lib esta no projeto, depois se você a declarou no topo do seu decorator, depois é só adicionar a tag body ou head que deveria funcionar.

  21. Jean Pierre Droguett Cortez 19 Jul 2010 12:52

    Washington parabens pelo trabalho...
    to tentando implementar aqui em um projeto que estou fazendo..
    e o design que eu tinha em mente é praticamente o mesmo que vc definiu porem sem a sessao da direita...
    só que eu to com um problema ao implementar o sitemesh...
    no menu superior eu tenho uns botões que eu chamo de modulos e de acordo com o modulo que o cara esta (quando ele clica em um modulo) o menu da esquerda tem que mudar de acordo com o modulo selecionado e as permissoes de usuario..
    eu fazia isso via ajax porem com o uso de decorators tudo ficou um pouco mais dificil..
    e nao tenho a menor idéia de como implementar isso no sitemesh.

    Ex: quando o cara clica no Usuario no menu do lado aparece Cadastrar Usuario, Buscar Usuario, Alter Usuario quando o cara clica em Membro no menu do lado aparece Cadastrar membro, bucar Membro e por ai vai Cara me da uma luz por favor

    Att,

    1. Washington Botelho autor 19 Jul 2010 14:32

      Fala Jean,

      Para cada tela diferente, você precisa criar um decorator.
      Se cada tela o menu da esquerda irá mudar, no seu decorator, você muda isso também atravéz do import, ou seja, cada decorator irá importar um página (menu) diferente:

      decorators/membro.jsp: (o import do menu será o menu*Membro.jsp)
      decorators/usuario.jsp: (o import do menu será o menu
      Usuario*.jsp)

      Depois só adicionar a regra no decorator.xml:

      <decorator name="usuario" page="usuario.jsp">
        <pattern>/usuario.jsp</pattern>
      </decorator>
      
      <decorator name="membro" page="membro.jsp">
        <pattern>/membro.jsp</pattern>
      </decorator>
      
Em resposta:
(cancelar)
Formate seu código utilizando Markdown.