WebToDate/Příručka vývojáře/Úlohy pro Plánovač úloh: Porovnání verzí
| Řádek 264: | Řádek 264: | ||
<nowiki></nowiki> | <nowiki></nowiki> | ||
</code> | </code> | ||
'''Vytvoření XML definice úlohy | '''Vytvoření XML definice úlohy''' | ||
Do souboru ''_registration_node.xml'' patří kód určující název úlohy, cestu ke spouštěnému skriptu, formulář administračního rozhraní a jednotlivé parametry úlohy. Kód definice pro příkladovou úlohu ''msg_count'' je následující: | |||
<code> | <code> | ||
Verze z 24. 9. 2009, 09:38
Úvod
Systém WebToDate obsahuje možnost spouštění tzv. plánovaných úloh v čase. V zásadě se jedná o to, že určité vybrané funkce – bloky kódu (např. vygenerování jedné stránky apod.) lze řetězit do sekvencí a tyto sekvence spouštět buď ručně (v tom případě jde v podstatě o spuštění velmi jednoduchého makra) nebo plánovaně v nastavených časech. Z hlediska administrátora je tato funkce popsána v uživatelské dokumentaci systému WebToDate.
Základní sada připravených úloh je k dispozici s každou instalací WebToDate, některé úlohy se navíc přidávají s instalací vybraných modulů. Plánovač úloh navíc počítá s tím, že lze přidávat nové uživatelské úlohy podle potřeb dané instalace.
Z hlediska programátora se jedná o vytváření bloků PHP kódu, pro který platí jistá pravidla (způsob předávání proměnných a informací o chybách) a ve kterých lze využít určité připravené knihovny. Takto vytvořené úlohy se pak registrují do systému. K nastavování parametrů, plánování a spouštění sekvencí úloh již existuje www rozhraní přímo v administračním rozhraní WebToDate.
Ve funkci Plánovač úloh se používá následující terminologie:
- Úloha – dále nedělitelný blok programového kódu, který vykonává nějakou činnost (např. vygenerování jedné stránky, spuštění jedné akce apod.)
- Sekvence – zřetězení úloh, které definuje pořadí vykonávání těchto úloh a případně jejich vzájemnou závislost. Nejjednodušší sekvence může přirozeně sestávat z jediné úlohy.
- Časování – předpis definující, kdy a jak se má daná sekvence spouštět v čase (jednorázově, periodicky apod.)
Příklady využití
Pomocí plánovaných úloh lze provádět např. následující činnosti:
- Periodické importy či exporty dat
- Periodické změny určité části webu a případné přegenerování stránek
- Rozesílání e-mailových upozornění v určených časových intervalech
Jednoduchým příkladem může být např. úkol, kdy chceme na úvodní stránku umístit údaj o tom, která osoba má ten den svátek. Informace se mění jednou denně a lze ji měnit automaticky, vhodným nástrojem je plánovaná úloha. Postup v tomto případě je zhruba následující:
- Založí se pevně jedna databázová tabulka naplněná údaji o jmeninách. Tyto údaje se nemění, není tudíž třeba vytvářet nějaký administrační interface pro změny dat.
- Založí se HTML fragment, který se trvale umístí na úvodní stránku do určené oblasti a v jehož těle se nachází formátovaný HTML kód s údajem o jmeninách.
- Vytvoří se zákaznická plánovaná úloha, která z databáze načte údaj o tom, kdo má daný den svátek. Tato úloha načtená jména zformátuje do požadovaného HTML tvaru a výsledným kódem přepíše tělo fragmentu z předchozího kroku (na to je k dispozici knihovní funkce, která má za parametr ID fragmentu a přepíše tělo tohoto fragmentu).
- Vytvoří se v plánovači nová sekvence, která v prvním kroku spustí tuto zákaznickou úlohu, ve druhém kroku spustí úlohu pro generování úvodní stránky (tato úloha je standardní součástí instalace WebToDate).
- Sekvence se načasuje na spouštění každý den po půlnoci.
Funkce a architektura
Rozdělení částí
Plánovač úloh lze logicky rozdělit na dvě části, a to na administrační a výkonnou část.
Administrační část
V administrační části se prostřednictvím webových formulářů definují sekvence, jejich jednotlivé úlohy, pro každou úlohu zpřesňující parametry. Každá sekvence může mít několik časových plánů spuštění.
Výkonná část
Výkonná část zajišťuje:
- Periodický proces kontroly, zda nemá být nějaká sekvence spuštěna (to lze alternativně suplovat okamžitým spuštěním sekvence tlačítkem v admin. rozhraní)
- Je-li sekvence určena ke spuštění a žádná jiná sekvence neběží, její rozložení na úlohy.
- Načtení parametrů úlohy, jejich předání pomocí objektu :ActiveTask.
- Zapnutí zachytávání běhových i syntaktických chyb, předání řízení výkonnému PHP kódu naplánované úlohy.
- Převzetí řízení zpět od výkonného kódu, zaprotokolování průběhu.
- Periodické předávání hlášení od běžící naplánované úlohy uživateli do webového formuláře.
Výkonná část je řešena objektově, jednotlivé třídy jsou přehledně uspořádány do komponent podle následujícího schématu:

Z hlediska programátora zákaznických naplánovaných úloh je zajímavá pouze komponenta Executabe, obsahující jedinou třídu ActiveTask. Kompletní popis komponent a tříd je uveden v dokumentu Plánovač úloh – návrh.
Případy užití
Pomineme-li administraci úloh a sekvencí, plánovač navenek nabízí rozhraní pro tyto případy užití:
- Okamžité spouštění sekvencí uživatelem administrační části aplikace WebToDate.
- Naplánované spouštění sekvencí operačním systémem (konkrétně prostřednictvím Windows Scheduled tasks nebo Cronu).

Platí, že vždy může být zpracovávána pouze jedna naplánovaná úloha (tedy i pouze jedna sekvence). Znamená to, že je ošetřen případ, kdy by chtěl actor (uživatel či OS) paralelně spustit další úlohu. Toho bude dosaženo následovně:
- Okamžité spuštění sekvence – uživateli administrační části je oznámeno, že musí posečkat, protože již probíhá jiná sekvence (lhostejno jestli spuštěná stejným uživatelem, jiným uživatelem, nebo OS).
- Naplánované spouštění sekvencí – OS by sotva dokázal adekvátně reagovat na takové oznámení. Naplánované spouštění bude proto rozděleno do dvou kroků:
- Pokud tam již není, vložení spouštěné sekvence do fronty.
- Pokud není aktivní jiná sekvence, postupné spouštění všech sekvencí ve frontě.
Vytvoření zákaznické úlohy pro plánovač úloh
Vytvoření nové zákaznické úlohy zahrnuje následující kroky:
- Vytvoření adresáře se soubory úlohy, tj. s
- XML definicí úlohy
- formulářem pro administrační rozhraní
- samotným výkonným PHP skriptem
Umístění adresáře se zákaznickou úlohou by mělo být scheduler/tasks/custom/novaá_úloha.
- Začlenění úlohy do seznamu nabízených plánovaných úloh,tzv. registrací. Ta se prakticky provádí přidáním XML uzlu popisujícího úlohu do XML souboru offlinedata/scheduler/registration.xml.
Bývá dobrým zvykem XML uzel s definicí úlohy uložit do samostatného souboru "_registration_node.xml" a tento rovněž přiložit do adresáře se soubory úlohy . Definice úlohy obsahuje:
- id (jednoznačný řetězec) úlohy
- typ (WTD, moduly, uživatelská) úlohy
- název úlohy
- cestu k formuláři úlohy, používanému v administrační rozhraní pro nastavení parametrů
- cestu ke výkonnému PHPskriptu úlohy
- informace o případných parametrech úlohy
Formulář pro administrační rozhraní zpravidla obsahuje jednoduchou HTML tabulku s formulářovými poli a skupinu hidden polí, řídících zpracování výstupu formuláře (hidden pole říkají, jaký formulářový prvek odpovídá jakému parametru úlohy).
Výkonný skript úlohy obsahuje kód, který je inkludován při vykonávání naplánované úlohy. Během běhu tohoto includovaného PHP skriptu dochází k zachytávání syntaktických i běhových chyb uvnitř skriptu, což usnadňuje ladění.
Skript má možnost pomocí instance třídy "ActiveTask":
- Získat hodnoty vložených parametrů (metoda :ActiveTask->getParam('nazev_parametru') ),
- Informovat o průběhu úlohy (metoda :ActiveTask->setAuxNote('zprava_uzivateli') ), např. "Generuji stránku Aktuality…". Hlášení je v reálném čase zveřejněno v administrační části plánovače úloh, pokud má obsluha otevřen příslušný formulář.
- Informovat o (ne)úspěšném ukončení úlohy, příp. s doplňujícími informacemi. Pokud proběhla úloha bez chyby, volá se metoda :ActiveTask->terminate('zprava_uzivateli'). Při výskytu chyby se volá metoda terminateWithError('chybova_zprava'). Tyto metody neukončují skript ani nevrací hodnoty, jen nastavují výsledek skriptu úlohy. Kromě těchto možností může běh skriptu samozřejmě skončit také díky syntaktické nebo běhové chybě. Všechny získané údaje o ukončení úlohy jsou jednak okamžitě zveřejněny v příslušné HTML stránce, jednak zaprotokolovány.
Vytvoření zákaznické úlohy
Postup je demonstrován na příkladu – vytvoření plánované úlohy, která přebírá jeden parametr (ID zprávy) a obsah této zprávy pak nahrazuje oznámením o celkovém počtu publikovaných zpráv, přítomných v databázi aplikace WebToDate. Úloha se bude jmenovat msg_count.
Vytvoření potřebných souborů. V příslušném nově vytvořeném adresáři ( /scheduler/tasks/custom/msg_count/ ) je nutné vytvořit soubory:
- s kódem formuláře pro administračním rozhraní ( _tf_msg_count.php )
- se samotným výkonným PHP kódem ( task_msg_count.php ).
- chceme-li formulář úlohy v administrační části lokalizovat do několika jazyků, je navíc nutné vytvořit soubor pro lokalizační konstanty ( /rdata/resources/webtodate/scheduler/tasks/msg_count/_localization_inc.php ), které budou v tomto formuláři použity.
- do adresáře je vhodné přidat také XML uzel s definicí úlohy ( _registration_node.xml ).
Vytvoření formuláře pro administrační rozhraní
Do souboru _tf_msg_count.php patří kód tisknoucí formulář, jehož pomocí se úloha v Plánovači nastavuje. Kód formuláře příkladové úlohy msg_count je následující:
<?php
$form = '
<table class="telo" cellpadding=0 cellspacing=0>
<tr>
<td class="flabelLeft" colspan=2>
' . loc('h_tf_msg_count_001') . '
<input type="hidden" value="2" name="par'. $inTaskOrder .'">
<input type="hidden" value="'.$inTaskId.'" name="tid'. $inTaskOrder .'">
<br><br>
</td>
</tr>
<tr>
<td class="flabel">
' . loc('h_tf_msg_count_010') . ' <small>(' . loc('h_tf_msg_count_011') . ')</small>
</td>
<td class="fvalue">
<input type="text" value="" name="h'. $inTaskOrder .'_1">
<input type="hidden" value="' . loc('h_tf_msg_count_010') . '" name="p'. $inTaskOrder .'_1">
<input type="hidden" value="string" name="v'. $inTaskOrder .'_1">
</td>
</tr>
<tr>
<td class="flabel">
' . loc('h_tf_msg_count_020') . '
</td>
<td class="fvalue">
<input type="text" value="" name="h'. $inTaskOrder .'_2">
<input type="hidden" value="' . loc('h_tf_msg_count_020') . '" name="p'. $inTaskOrder .'_2">
<input type="hidden" value="i4" name="v'. $inTaskOrder .'_2">
</td>
</tr>
</table>
';
echo( $form );
?>
Význam formulářových polí je následující (uvědomme si technické řešení administrační části – formuláře všech typů úloh jsou umístěny v jednom html kódu a pouze přepínány):
Přiřazení názvu úlohy k pořadovému číslu formuláře:
- <input type="hidden" value="'.$inTaskId.'" name="tid'. $inTaskOrder .'"></tt.
Určení celkového počtu parametrů úlohy (zde 2 parametry):
- <input type="hidden" value="2" name="par'. $inTaskOrder .'">
Pole pro vložení hodnoty n-tého parametru:
- <input type="text" value="" name="h'. $inTaskOrder .'_n">
Pole pro vložení komentáře k n-tému parametru (komentář není v současné verzi využíván):
- <input type="hidden" value="comment '" name="p'. $inTaskOrder .'_n">
Pole pro určení datového typu n-tého parametru (zde se jedná o řetězec):
- <input type="hidden" value="string" name="v'. $inTaskOrder .'_n">
Pozn.: Jestliže v XML definici úlohy neuvedete odkaz na takovýto formulář pro zadávání parametrů, úloha bude přesto editovatelná – v administrační části bude použit jednoduchý automaticky vygenerovaný formulář.
Vytvoření lokalizačních konstant
Do lokalizačního souboru ( /rdata/resources/webtodate/scheduler/tasks/msg_count/_localization_inc.php ) je potřeba umístit lokalizované texty použité ve formuláři a hlášení o výsledku úlohy. Kód pro příkladovou úlohu msg_count je následující:
<?php
/**
* skript slouzi k lokalizaci formulare i vykonne casti ulohy
*
* @package task_msg_count.php
* @author
*/
define('h_tf_msg_count_001',"Nastavení parametrů úlohy <b>Počet publikovaných zpráv</b>:");
define('h_tf_msg_count_010','Text nahrazující tělo zprávy');
define('h_tf_msg_count_011','Musí obsahovat %s - nahrazuje se zjiątěným počtem zpráv.');
define('h_tf_msg_count_020','ID nahrazované zprávy');
define('c_task_msg_count_success','Vykonání úlohy Počet publikovaných zpráv proběhlo úspěąně.');
define('c_task_msg_count_norecord','Nebyla nalezena zpráva s uvedeným ID (%d).');
?>
Vytvoření samotného výkonného skriptu úlohy
Do souboru task_msg_count.php patří kód, který bude vykonán při spouštění.
V něm je nutné inicializovat novou instanci třídy ActiveTask, která umožňuje přístup k zadaným parametrům úlohy, ale lze také pomoci ní určit výsledek úlohy. Pomocí metody setAuxNote( string:message ) lze navíc informovat uživatele o průběhu úlohy; s každým jejím voláním se uživateli zobrazí předané hlášení o vykonávané činnosti, o průběhu apod.
Kód skriptu příkladové úlohy msg_count je následující:
<?php
/**
* toto je skript pro planovac ulo
* slouzi k nahrazeni tela zpravy urcenou hlaskou oznamujici o poctu publikovanych zprav
*
* @package msg_count.php
* @param int text vkladany do tela zpravy messageText
* @param int ID prepisovane zpravy messageID
*/
// Nacteme lokalizacni konstanty
require_once( buildPath( getResourcesPath(), 'webtodate/scheduler/tasks/msg_count/_localization_inc.php' ) );
// Vytvorime objekt, predstavujici tuto ulohu
$objActTask = new ActiveTask();
// Zjisteni a osetreni vstupnich parametru
$strMsgText = $db->check_quotes( $db->check_slashes( $objActTask->getParam('messageText') ) );
$intMsgID = (int) ($db->check_quotes( $db->check_slashes( $objActTask->getParam('messageID') ) ));
// Vykonani ulohy
// - zjisteni poctu publikovanych zprav (jen zpravy, ne fragmenty)
$sql = "select COUNT(ID) as POCET from NEWSDB where KOREKTURA=1 and HTMLFRAGMENT=0";
$res = $db->query ($sql); $rec = $db->fetch_array($res);
$intCount = $rec['POCET'];
// - sestaveni zpravy
$strMessage = sprintf( $strMsgText, $intCount );
// - nahrazeni tela zpravy s ID $intMsgID novym obsahem
$sql = "update NEWSDB set TELO='" . $strMessage . "' where ID=" . $intMsgID;
$res = $db->query ($sql);
// Vyhodnoceni
if ( $db->affected_rows($res) ):
// zaznam zpravy byl zmenen
$objActTask->terminate( loc('c_task_msg_count_success') );
else:
// nebyl zmenen zadny radek, tzn. zprava s urcenym ID neexistuje; urcime, ze se vyskytla chyba
$objActTask->terminateWithError( sprintf(loc('c_task_msg_count_norecord'), $intMesgID) );
endif;
?>
Vytvoření XML definice úlohy
Do souboru _registration_node.xml patří kód určující název úlohy, cestu ke spouštěnému skriptu, formulář administračního rozhraní a jednotlivé parametry úlohy. Kód definice pro příkladovou úlohu msg_count je následující:
<task id="msg_count" originator="WTDCustom">
<name>Pocet generovanych zprav</name>
<!-- relativni cesta ke skriptu, ktery vykona ulohu -->
<executable>scheduler/tasks/custom/msg_count/task_msg_count.php</executable>
<form>
<!-- relativni cesta k formulari, ktery edituje parametry ulohy -->
<code>scheduler/tasks/custom/msg_count/_tf_msg_count.php</code>
<!-- relativni cesta k souboru s lokalizacnimi konstantami pro formular -->
<localization>rdata/resources/webtodate/scheduler/tasks/msg_count/_localization_inc.php</localization>
</form>
<parameter>
<!-- poradi parametru -->
<orderid>1</orderid>
<!-- nazev parametru -->
<key>messageText</key>
<!-- popis parametru -->
<name>Text vkladany do zpravy</name>
<!-- datovy typ parametru -->
<type>string</type>
<!-- implicitni hodnota -->
<default>Na serveru je k dispozici %s publikovanych zprav.</default>
</parameter>
<parameter>
<orderid>2</orderid>
<key>messageID</key>
<name>ID nahrazovane zpravy</name>
<type>i2</type>
<default></default>
</parameter>
</task>
Doplnění XML definice do globálního souboru s definicemi úloh Plánovače
Při nastavování úlohy se v administračním rozhraní nabízejí všechny ty úlohy, jejichž definici obsahuje soubor /offlinedata/scheduler/registration.xml. Sem je tedy nutné vložit definici úlohy (obsah souboru _registration_node.xml), a to na úroveň ostatních elementů task.
Toto jsou všechny kroky potřebné pro vytvoření úlohy pro Plánovač úloh.
Knihovny a třídy
Třída ActiveTask
Tato třída je součástí komponenty Executable. Komponenta má využití pouze ve veřejné části Plánovače úloh, a to v PHP skriptu s naplánovanou úlohou. Vývojáři naplánované úlohy nabízí rozhraní pro
- převzetí vstupních parametrů, nutných pro běh úlohy.
- odevzdání hlášení o výsledku běhu úlohy.
Komponenta je fyzicky umístěna ve skriptu scheduler/classes/executable/activetask.php. Rozhraní komponenty je implementováno zejm. veřejnými metodami getParam, terminate (popř. terminateWithError) právě třídy ActiveTask.
Samotná třída ActiveTask představuje právě spuštěnou úlohu.
Metody a vlastnosti
konstruktor
Nastavuje vlastnost $objActiveTask podle statické metody Task->getActiveTask(). Dále může nastavit vlastnosti jako $strName podle $objActiveTask->getName() atp.
getParameter, getParameterByPos
Obsahují vnořená volání metod $objActiveTask->getParam(…), resp. $objActiveTask->getParamByPos(…).
setAuxNote
Metoda se pokusí nastavit specifikovaný řetězec jako pomocnou poznámku pro proces plánovače:
$myProcess = new ProcProgress::Process("SCHEDULER");
$myProcess->setAuxNote(aNote);
terminate
Slouží k ukončení skriptu úlohy se zdárným koncem:
$objActiveTask->setStatus(Task->STATUS_OK);
$objActiveTask->setMsg(aMsg);
return; //Odchod z inkludovaného souboru
terminateWithError
Slouží k ukončení skriptu úlohy při uživatelské chybě:
$objActiveTask->setStatus(Task->STATUS_ERR_USER);
$objActiveTask->setMsg(aMsg);
return; //Odchod z inkludovaného souboru
Použití v PHP skriptu úlohy
Úloha bude mít následující strukturu. Do takovéto podoby je třeba převést všechny současné úlohy. Používání metody setCurrActivity je nepovinné.
<?php
$activeTask = new ActiveTask();
//Zjištění parametrů (možno provádět v celém skriptu)
$f_param_1 = $activeTask->getParam("param_1");
$f_param_2 = $activeTask->getParam("param_2");
...
$f_param_n = $activeTask->getParam("param_n");
//První polovina úlohy
...
$activeTask->setAuxNote("Polovina úlohy je za námi");
//Druhá polovina úlohy
...
//Ukončení úlohy a detekce uživatelských chyb
if(!$blnErrDuringTask){
$activeTask->terminate("Úloha proběhla v pořádku.");
else{
$activeTask->terminateWithError("V úloze došlo k té a té chybě.");
}
?>
Třídy pro přístup k databázi a souborům
Pro přístup k databázi a k souborům je třeba používat globální proměnné $db a $fa, viz kapitoly Databázová knihovna a Souborová knihovna.
Další společné funkce
Při tvorbě úloh pro plánovač jsou k dispozici také další funkce a procedury uložené v souboru /scheduler/includes/CommonFunctions.php. Zde je jejich reference:
- ReplaceBody ( inNewsId, inNewBody )�- nahrazuje tělo zprávy s ID inNewsId textem inNewBody včetně ošetření, zda jde o zprávu uloženou v databázi nebo na disku
- replaceNewsBodyInDB ( inNewsId, inNewBody )�- interní procedura využívaná procedurou ReplaceBody; nahrazuje v databázi tělo zprávy s ID inNewsId textem inNewBody
- replaceNewsBodyInFile ( inNewsId, inNewBody )�- interní procedura využívaná procedurou ReplaceBody; nahrazuje v souboru tělo zprávy s ID inNewsId textem inNewBody
- getPathToNewsFile ( inNewsId )�- načítá z databáze podle ID (inNewsId) cestu ke zprávě z db pole NEWSDB:CESTA
- makeTextsPath ( newsPath )�- přidává před předanou cestu newsPath cestu do adresáře s texty
- generateSinglePage ( inPageId )�- přegenerovává stránku s ID inPageId