PZR420 - ćw - temat E

Tematyka tematu E:
http, REST, http od strony serwera, toolkity webowe,
wykorzystanie serwisów społecznościowych we własnych programach,
?!?!?!

AOLserver - ciekawy serwer http/app

Strona główna www.aolserver.com
NaviServer to fork aolservera; naviserver docs (trochę lepsza dokumentacja!).
Instalacja: polecam wersję 4.0.10 - aolser4.0.10_bin.tar.gz.
Artykuł "Introduction to AOLserver": Part 1, Part 2

Cechy aolserwera:
  1. jest wysokowydajny...
  2. API aolserwer-a
    komendy ns_* ... raczej niskopoziomowe;
    aolser nie jest (w zasadzie) przeznaczony do bezpośredniego tworzenia app webowych,
    tylko do tworzenia toolkitów/frameworków webowych, nad którymi później
    buduje się app webowe; przykłady: OpenACS, .LRN, OSSWEB
  3. moduły
    skrypty Tcl umieszczone w katalogu server1/modules/tcl;
    moduły inicjalizuja wątki (ładują pakiety lub inaczej definiują procedury rozszerzające API);
    moduły mogą też definiować filtry i proc. obsługi url-i itp
  4. strony www: pliki *.adp i *.tcl
    są to pliki w katalogu server1/pages;
    klienci podaja url-e odwołujące się do tych plików, np:
       http://localhost:8000/qqq/np01.tcl
    pliki ADP zawierają html ze wstawkami <% skrypt %>, <%= wyrażenie%> itp
    skrypty tcl używają komend ns_*, w szczególności zwracają wynik przez:
       ns_return 200 text/html "<p>witaj $login !!!</p>"
  5. filtry i proc obsługi url-i
    moduł może zdef proc obsługującą konkretne url-e;
    filtr to warstwa, przez którą przebija się żądanie http, w rzeczywistości jest to
    procedura która może przekazać żadanie dalej lub obsłużyć je na miejscu;
    filtry pozwalaja w sposób calkowicie przezroczysty rozwiązać problem AAS
    czyli Authentication/ Authorization/ Session Mgm (nazwa wymyślona przez prowadzącego;-);)
  6. ???
Inne cechy aolserwera:
  1. obsługa sesji
    aolser v. 4.0.10 nie dostarcza żadnego mechanizmu "obsługi sesji" !!!
    trzeba go doinstalować dodając pewne moduły (które bardzo łatwo napisać samodzielnie!);
    zmienne sesyjne można m.in. trzymać w tzw zmiennych nsv (są to zmienne dzielone,
    dostępne dla wszystkich wątków)
  2. stan sesji MUSI być przechowywany w "danych tclowych" czyli w stringach!!!
    (w zmiennych nsv, w zewn pliku, w bazie danych itp)
    można używać obiektów Javy (poprzez tclBlend) i XOTcl, jednak NIE MOŻNA
    przechowywać w takich obiektach stanu sesji ...
    ... chyba że gotowi jesteśmy serializować/deserializować te obiekty np do zmiennych nsv
  3. uwierzytelnianie http typu BASIC
    aolser ma to wbudowane, przy czym prawa i użytkowników trzyma się
    (podobnie jak robi to unix) w plikach server1/modules/nsperm/passwd itd
  4. konfiguracja serwera
    plik konfiguracyjny (sample-config.tcl) jest skryptem, a zatem może być inteligentny!!
    używa komend ns_section i ns_param do definiowania sekcji (tworzą drzewo)
    oraz parametrów w sekcjach, każdy parametr ma nazwę i wartość
  5. ???
Zastosowania aolserwera:
  1. baza, nad którą można zbudować framework/toolkit webowy...
    przykład takiego toolkitu: openacs.org
    toolkity bazujące na openacs: dotlrn.org, www.project-open.com, www.project-open.org
  2. serwer REST
  3. zastosowania specjalne (proxy)
  4. ???


----------------------------------------------------------------
Przykład 01 - pokazywanie wydruków zewnętrznych programów.
Oba pliki robią dokładnie to samo!
--- np01.adp ---
<h1>Informacje</h1>
<p>ls -l:
<pre><%
ns_puts "[exec ls -l]"
%></pre>
</p>
<p>ps -Oppid,pcpu,pmem:
<pre><%= [exec ps -Oppid,pcpu,pmem] %></pre>
</p>
--- np01.tcl ---
ns_return 200 text/html "
<h1>Informacje</h1>
<p>ls -l:
<pre>[exec ls -l]</pre>
</p>
<p>ps -Oppid,pcpu,pmem:
<pre>[exec ps -Oppid,pcpu,pmem]</pre>
</p>
"

----------------------------------------------------------------
Przykład 02 - przekazywanie danych z formularza.
--- np02.htm ---
<form method="GET" action="np39.tcl">
tekst1: <input type="text" name="tekst1"><br>
tekst2: <input type="text" name="tekst2"><br>
<input type="submit" value="submit">
</form>

--- np02.tcl ---
set tekst1 [ns_queryget tekst1]
set tekst2 [ns_queryget tekst2]

ns_return 200 {text/html; charset=utf-8} "
<p>tekst1=$tekst1</p>
<p>string len= [string len $tekst1]</p>
"


----------------------------------------------------------------
Przykład 03 - moduł rejestrujący proc obslugi url-a.
Plik mod03.tcl należy umieścic w katalogu modules/tcl;
używa się tego tak:
  http://localhost:8000/hello?login=Jan
--- mod03.tcl ---
ns_register_proc GET /hello hello
proc hello {} {
set login [ns_queryget login "?"]
ns_return 200 {text/html; charset=utf-8} "
<p>witaj $login !</p>
"
}



----------------------------------------------------------------
Przykład 04 - pokazujący jak trzymać stan sesji w ciasteczku u klienta ...
(niektórzy uważają, że tak się właśnie powinno robić ... patrz: REST)
Dowolne zmienne sesyjne znajdują się w tablicy "sess".
Tablice tę można modyfikować miedzy sessLoad i sessSave,
(po sessSave jest nadal dostępna, ale zmiany nie są uwzględniane).
-- mod04.tcl ---

## zm. sesyjne w cookies..
# - sposob uzycia: miedzy sessLoad i sessSave
# jest dostepna tablica sess ze zmiennymi sesyjnymi!
#
proc sessLoad {} {
upvar sess sess1
array set sess1 [ns_getcookie "sess" ""]
}
proc sessSave {} {
upvar sess sess1
ns_setcookie "sess" [array get sess1]
}

## uproszczona impl. ns_set/getcookie - AOLserver 4.0.10 tego NIE MA !!!
#
proc ns_getcookie {key defa} {
set x [ns_set iget [ns_conn headers] Cookie]
if {$x!=""} {
set x1 [split $x ";"]
set x2 {}
foreach e $x1 {lappend x2 [string trim $e]}
set x3 {}
foreach e $x2 {lappend x3 [split $e "="]}
foreach e $x3 {
foreach {k1 v1} $e break
if {$key==$k1} {return [ns_urldecode $v1]}
}
}
return $defa
}
proc ns_setcookie {key val} {
ns_set put [ns_conn outputheaders] Set-Cookie $key=[ns_urlencode $val]
# - mozna kilka razy wywolac ns_setcookie z ROZNYMI kluczami
# w jednej transakcji http ...
}

# --- np04.tcl ---

# logika biznesowa
sessLoad
append sess(tekst1) {123 }
# operacja na zm. sesyjnych ...
sessSave

# prezentacja
ns_return 200 text/html "
<p>
tekst1= $sess(tekst1)<br>
pid= [pid]<br>
</p>
"

# --- np04.adp ---

# logika biznesowa
<%
sessLoad
append sess(tekst1) {123 }
sessSave
%>

<p>
tekst1= <%=$sess(tekst1)%><br>
pid= <%=[pid]%><br>
</p>



----------------------------------------------------------------
Przykład 05 - pokazujący jak w sposób przezroczysty obsłużyć AAS ...
Wszystkie url-e przechodzące przez np05 będą uwierzytelniane,
i jeśli uwierzytelnianie się powiedzie to otrzymają sess_id,
który będzie pamiętany w ciasteczku u klienta
(po stronie serwera jest to zmienna globalna, która trzeba dekl. przez "global sess_id")
# --- mod05.tcl ---
ns_register_filter postauth GET /np05/* filter_np05
ns_register_filter postauth POST /np05/* filter_np05
proc filter_np05 {conn when} {
opensession
}

proc loginform p {
ns_return 200 "text/html" [concat $p {
<form method=POST>
login: <input type="text" name=login><br>
passwd: <input type="password" name=passwd><br>
<input type=submit value=login>
</form>
}]
}
proc opensession {} {
global sess_id
set sess_id [ns_getcookie sess_id ""]
set login [ns_queryget login ""]
set passwd [ns_queryget passwd ""]

if {$sess_id=="" && $login==""} {
loginform {<p>podaj login i haslo...</p>}
return "filter_return"
}
if {$sess_id=="" && $login!=""} {
# uwierzytelniamy i ew. tworzymy sesje
#
if {$login=="qqq" && $passwd=="qqq"} {
# trzeba to uogolnic !!!
set sess_id "sess[uuid::uuid generate]"
# tu uzywa sie pakietu uuid z tcllib do wygenerowania sess_id
ns_setcookie sess_id $sess_id
nsv_set $sess_id login $login
nsv_set $sess_id login_time [ns_time]
return "filter_ok"
} else {
loginform {<p>bledny login i/lub haslo!</p>}
return "filter_return"
}
}
if {$sess_id!=""} {
# sprawdzamy czy taka sesja istnieje
#
if {! [nsv_exists $sess_id login]} {
ns_setcookie sess_id ""
loginform {<p>ta sesja juz nie istnieje!</p>}
return "filter_return"
} else {
nsv_set $sess_id access_time [ns_time]
return "filter_ok"
}
}
}


----------------------------------------------------------------
Przykład 06 - strona zwracająca obraz tworzony przy pomocy javy...
# --- np06.tcl ---

# proc pomoc.
proc setColor {r g b} {
upvar g1 g1_
$g1_ setColor [java::new {java.awt.Color int int int} $r $g $b]
}

# glowne obiekty
set bi1 [java::new java.awt.image.BufferedImage 700 230 5]
set g1 [$bi1 getGraphics]
set baos [java::new java.io.ByteArrayOutputStream]

# rysujemy...
setColor 255 255 255
$g1 fillRect 0 0 700 230
setColor 0 255 0
iterate i 40 {
$g1 drawLine 100 100 700 [expr {0+$i*5}]
}

# odsylamy obraz do przegladarki
$baos reset; java::call javax.imageio.ImageIO write $bi1 "png" $baos
ns_return 200 "image/png" [$baos {toString int} 0]
# metoda "toString 0" tworzy string kompatybilny z Tcl!



----------------------------------------------------------------
Zadanie 52 (obsługa sesji gdy używamy http auth)
Napisz moduł aolservera tworzący zm. globalną "sess_id" dla kazdego żądania
do aplikacji np52 (np http://localhost:8000/np52/plik1.adp),
przy założeniu, że używa się "http auth", i że 1 użytkownik ma 1 sesje.
Wskazówka: patrz opis "ns_conn authuser" i "ns_register_filter"

----------------------------------------------------------------
Zadanie 53 (stan sesji w bazie danych)
Zamien procedury sessLoad/sessSave na sessLoadFromDb/sessSaveToDb
zapamiętujące stan sesji w tabeli bazy danych po stronie serwera.
Do obsługi bazy danych użyj komendy ns_db ...

----------------------------------------------------------------
Zadanie 54 (rysowanie dowolnego wykresu słupkowego)
Zaprogramuj "serwer wykresów", który potrafi narysować wykres słupkowy
dla dowolnych danych wejściowych przekazanych przez parametr url-a.
Serwer ten ma być oparty na aolser, czyli ma to być "strona"
z typem mime odpowiedzi "image/png";
obrazek twórz przy pomocy biblioteki javowej JFreeChart (?),
używanej poprzez tclBlend; jak to robić patrz przykład 06;
pokaż także zastosowanie tego serwera ...
(Uwaga: to jest przykład użycia javy spod aolser!)

----------------------------------------------------------------
Zadania 55 (???)
???




toolkit webowy OpenACS

(!!! NIEDOKOŃCZONE !!!)




servlety i tomcat

(!!! NIEDOKOŃCZONE !!!)

Specyfikacja servletów v. 2.4
Książka "Thinking in J2EE"  (rozdział o servletach)
Instalacja tomcat-a

Skrypty Jacl jako servlety

// --- Jacl1.java ---

// servlet pozwalający używać skryptów Jacl jako servletów ...
// do katalogu WEB-INF/lib trzeba skopiować pliki: jacl.jar i tcljava.jar

import java.io.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Jacl1 extends HttpServlet {
public final String dir= "/home/mhanckow/jacl_servlet";
// katalog w ktorym zainstalowano app webowa z tym servletem

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
javax.servlet.http.HttpSession sess= request.getSession();
tcl.lang.Interp interp= (tcl.lang.Interp)sess.getAttribute("tclinterp");
boolean newSess= false;
if (interp==null) {
interp= new tcl.lang.Interp();
sess.setAttribute("tclinterp", interp);
newSess= true;
}
try {
interp.setVar("req", tcl.lang.ReflectObject.newInstance(interp, request.getClass(), request) ,0);
interp.setVar("res", tcl.lang.ReflectObject.newInstance(interp, response.getClass(), response) ,0);
interp.setVar("sess", tcl.lang.ReflectObject.newInstance(interp, sess.getClass(), sess) ,0);
String dir1= dir+request.getContextPath();
if (newSess) interp.evalFile(dir1+"/newsess.tcl");
interp.evalFile(dir1+request.getServletPath());
} catch(tcl.lang.TclException e) {}
}
}

<!--- deklaracja servletu w WEB-INF/web.xml
--->
<servlet>
<servlet-name>Jacl1</servlet-name>
<servlet-class>Jacl1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Jacl1</servlet-name>
<url-pattern>*.tcl1</url-pattern>
</servlet-mapping>



# --- newsess.tcl ---

## skrypt inicjalizujący nowo tworzone "interpretery sesyjne"
#
package re java


# --- np01.tcl1 ---

## podstawowy przyklad ...
# - skrypty .tcl1 tworza interp sesyjny (w momencie utworzenia sesji) !!!
# - zmienne oraz jakiekolwiek inne zasoby interpa sa "sesyjne" !!!
#

$res setContentType "text/html"
set out [$res getWriter]

$out println "<html><head></head><body>"

$out println "<p>A ku ku !!!</p>"

set err [catch { # wykonanie kodu pod dbg...

if {[info exists zm]} {incr zm} {set zm 3000}
$out println "<p>zm=$zm</p>"

} errInfo]; if {$err} {$out println "<pre>$errInfo</pre>"}

$out println "</body></html>"
$out close



----------------------------------------------------------------
Zadanie 56 (skrypty .tcl2, nie tworzące interpa sesyjnego)
Dodaj servlet obslugujący skrypty .tcl2, który tworzy interpreter Jacl
przy każdym żadaniu http (czyli nie tworzy "interpa sesyjnego" !).
Kiedy takie skrypty maja przewagę nad .tcl1 ???

----------------------------------------------------------------
Zadanie 57 (???)
???



Facebook, GoogleMaps itp
z pkt. widzenia programisty...

(!!! NIEDOKOŃCZONE !!!)