Hlavní navigace

Cocoon v příkladech: Programujeme vlastní komponenty (1)

2. 6. 2004
Doba čtení: 6 minut

Sdílet

V tomto dílu se podíváme na to, jak (snadno) lze v Cocoonu naprogramovat vlastní komponenty. Začneme těmi nejjednoduššími - vytvoříme si vlastní selektor a akci. Rozborem zdrojového kódu a příkladem použití si také zopakujeme, jak se selektory a akce chovají, navíc získáme jistotu v rozhodování, kdy je vhodné tyto typy komponent nasazovat.

Popis komponent

Není snadné najít rozumný problém, který by již v Cocoonu nebyl vyřešen (tj. pro který by už nebyla nějaká komponenta hotová) a řešení kterého by zároveň vedlo k vytvoření jednoduché komponenty vhodné jako příklad. Zvolme si tedy „akademický“ příklad, kdy budeme chtít vytvořit nějakou webovou aplikaci či prezentaci, která bude citlivá na „denní dobu“ – její obsah nebo forma bude záviset na tom, zda je ráno, nebo večer (dle lokálního času serveru). Nic takového Cocoon ještě neobsahuje.

Selektor a jeho anatomie

Ačkoliv jsme selektory potkali již dříve, nebude na škodu stručná rekapitulace. Selektory jsou komponenty Cocoonu, které na základě vyhodnocení jednoduchého logického výrazu umožňují v mapě větvení typu podmíněného příkazu (if-then-else) nebo přepínače (select-case).

Selektor patří mezi ty nejjednodušší komponenty. Stačí implementovat jednu metodu rozhraní vracející logickou hodnotu. Komponenta navíc ani nepracuje s XML, takže její implementace je (obvykle) hračkou. Implementujme selektor, který „úspěšně“ vyhodnotí řetězec „rano“, pokud je lokální čas na serveru mezi 6:00 a 9:59, a řetězec „vecer“, pokud bude mezi 19:00 a 22:59. Úspěšné vyhodnocení znamená návrat hodnoty „true“, jinak se vrací „false“. Komponenta by mohla vypadat třeba takto:

package org.apache.cocoon.selection;

import org.apache.cocoon.selection.Selector;
import java.util.Map;
import java.util.Calendar;
import
org.apache.avalon.framework.parameters.Parameters;

public class MujSelektor implements Selector {
  public boolean select( String expression,
      Map objectModel, Parameters parameters) {

    Calendar cal = Calendar.getInstance();
    int hour = cal.get(Calendar.HOUR_OF_DAY);

    if (expression.equals("rano")) {
      return hour >= 6 && hour <= 9;
    }
    else if (expression.equals("vecer")) {
      return hour >= 19 && hour <= 22;
    }

    return false;
  }
}

Jak vidíte, komponenta je opravdu jednoduchá. V implementované metodě „select“ je nejzajímavější parametr „expression“, což je řetězec předávaný z mapy do komponenty. Poslední parametr by byl užitečný, pokud bychom chtěli komponentu parametrizovat. Zdrojový kód komponenty umístíme jako soubor „MujSelektor.java“ do adresáře „…/src/java/or­g/apache/coco­on/selection“ k ostatním selektorům, kde „…“ je základní adresář Cocoonu (obvykle „~/cocoon-2.1.4“), a rebuildujeme Cocoon příkazem „./build.sh“ v základním adresáři Cocoonu. Komponentu pak použijeme v mapě takto:

<?xml version="1.0"?>
<map:sitemap
xmlns:map="http://apache.org/cocoon/sitemap/1.0">

<!-- === Deklarace komponent === -->

  <map:components>

    <map:selectors default="browser">
      <map:selector name="denni-doba"
src="org.apache.cocoon.selection.MujSelektor"/>
    </map:selectors>
  </map:components>

<!-- === Roury === -->

  <map:pipelines>

    <map:pipeline>

      <map:match pattern="">
        <map:select type="denni-doba">
          <map:when test="rano">
            <map:read src="rano.html"/>
          </map:when>

          <map:when test="vecer">
            <map:read src="vecer.html"/>
          </map:when>
          <map:otherwise>
            <map:read src="jinak.html"/>
          </map:otherwise>

        </map:select>
      </map:match>

    </map:pipeline>
  </map:pipelines>
</map:sitemap>

Naši komponentu je nutné v mapě deklarovat, použití je pak snadné. Pokud je úspěšný řetězec „rano“, pošle server klientovi soubor „rano.html“, pokud je úspěšný řetězec „vecer“, pošle se soubor „vecer.html“. V ostatních případech se pošle soubor „jinak.html“. Rád bych zdůraznil, že náš selektor je možné použít v libovolném místě roury. Můžeme tedy například zvolit v závislosti na denní době XSLT stylesheet, zatímco ostatní komponenty roury mohou být společné. Mohli bychom také vytvořit rouru pro kaskádový styl, kde by se pod zdánlivě jedním názvem skrývalo několik stylů závislých na denní době.

Komponenta typu „akce“

Selektor není jediný typ komponenty, kterým lze vyřešit náš příklad. Akce patří k nejčastěji programovaným komponentám Cocoonu. Jsou pořád ještě dosti jednoduché, nicméně mocnější než výše změněné selektory. Komponenty typu „akce“ přidávají do roury jednu nebo více dvojic klíč-hodnota. V rouře se pak lze na hodnoty odkazovat tak, že uvedeme klíč ve složených závorkách. V nejjednodušším případě je to opět javovská třída implementující jednu metodu rozhraní „Action“. Výsledek se vrací jako javovský typ „Map“. Podívejme se, jak by mohla vypadat naše komponenta:

package org.apache.cocoon.acting;

import org.apache.cocoon.acting.Action;
import org.apache.cocoon.environment.Redirector;
import
org.apache.cocoon.environment.SourceResolver;
import java.util.Map;
import java.util.HashMap;
import java.util.Calendar;
import
org.apache.avalon.framework.parameters.Parameters;

public class MojeAkce implements Action {

  public final static String DOBA = "denni-doba";

  public Map act( Redirector redirector,
      SourceResolver resolver,
      Map objectModel, String source,
      Parameters parameters) {

    Calendar cal = Calendar.getInstance();
    int hour = cal.get(Calendar.HOUR_OF_DAY);
    Map returnMap = new HashMap();

    if (hour >= 6 && hour <= 9) {
      returnMap.put(DOBA,"rano");
    }
    else if (hour > 9 && hour < 19) {
      returnMap.put(DOBA,"den");
    }
    else if (hour >= 19 && hour <= 22) {
      returnMap.put(DOBA,"vecer");
    }
    else {
      returnMap.put(DOBA,"noc");
    }

    return returnMap;
  }
}

Komponenta bude vracet řetězec určený klíčovým slovem „denni-doba“. V závislosti na hodině lokálního času se bude vracet řetězcová hodnota „rano“, „den“, „vecer“ nebo „noc“. V mapě pak použijeme vrácenou hodnotu jako část názvu odesílaného souboru. Budeme odesílat „rano.html“, „den.html“, „vecer.html“ nebo noc.html

<?xml version="1.0"?>
<map:sitemap
xmlns:map="http://apache.org/cocoon/sitemap/1.0">

<!-- === Deklarace komponent === -->

  <map:components>

    <map:actions>
      <map:action name="doba"
src="org.apache.cocoon.acting.MojeAkce"/>
    </map:actions>
  </map:components>

<!-- === Roury === -->

  <map:pipelines>

    <map:pipeline>

      <map:match pattern="">
        <map:act type="doba">
          <map:read src="{denni-doba}.html"/>
        </map:act>
      </map:match>

    </map:pipeline>
  </map:pipelines>
</map:sitemap>

Komponentu je potřeba umístit do adresáře „src/java/org/co­coon/acting“ a rebuildovat Cocoon. V mapě je pak nutné naši komponentu deklarovat. Opět bych rád zdůraznil, že je čistě na vás, jak páry klíč-hodnota, vracené akcí, v mapě použijete. Často se např. tato hodnota předává do XSLT stylesheetů jako parametr.

root_podpora

Závěr

Závěrem bych rád upozornil, že výše zmíněné příklady jsou jen ukázkou komponent (komponenty jsou však plně funkční). Pokud bychom dělali komponenty skutečné, bylo by vhodné se zamyslet na tím, zda by komponenty byly parametrizovatelné nebo konfigurovatelné a jaké další služby po nich budeme požadovat. Dobrým zvykem je, aby bylo umožněno alespoň logování (komponenta v tomto případě musí být potomkem třídy AbstractLogEna­bled). Ve výše zmíněných adresářích existují i abstraktní třídy s různými úrovněmi předdefinovaných služeb, které můžeme použít pro odvození třídy vytvářené komponenty. Rozhodně bych pro inspiraci doporučoval prostudovat zdrojový kód standardních komponent Cocoonu. Komponenty obsažené v adresářích „acting“ a „selection“ jsou většinou spíše jednodušší, takže by s tím neměly být větší problémy.

Všechny potřebné soubory můžete nalézt v archivu cocoon13.zip. Pokud jej rozbalíte v základním adresáři Cocoonu se zachováním cest a z tohoto adresáře spustíte rebuild, budete mít pří dalším spuštění Cocoonu k dispozici dvě primitivní aplikace na ověření činnosti výše zmíněných komponent. Aplikace se budou volat: „http://local­host:8888/selek­tor/“ resp. „http://local­host:8888/akce/“ (nezapomeňte na ukončovací lomítka!). V příštím dílu se podíváme na tvorbu složitějších komponent – naprogramujeme si transformátor.

Byl pro vás článek přínosný?