Scala + NetBeans AKA: Nowoczesne obiektowe języki programowania i środowiska programistyczne na przykładzie Scali i środowiska NetBeans

Podobne dokumenty
Przygotował: Jacek Sroka. PO* - Scala (iteratory, leniwość, view bounds i konwersje)

Wątki. Definiowanie wątków jako klas potomnych Thread. Nadpisanie metody run().

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

Scala + NetBeans AKA: Nowoczesne obiektowe języki programowania i środowiska programistyczne na przykładzie Scali i środowiska NetBeans

Programowanie obiektowe

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

Współbieżność w środowisku Java

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

Obliczenia równoległe i rozproszone w JAVIE. Michał Kozłowski 30 listopada 2003

Języki i Techniki Programowania II. Wykład 7. Współbieżność 1

Stworzenie klasy nie jest równoznaczne z wykorzystaniem wielowątkowości. Uzyskuje się ją dopiero poprzez inicjalizację wątku.

Programowanie równoległe i rozproszone. Monitory i zmienne warunku. Krzysztof Banaś Programowanie równoległe i rozproszone 1

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Język Java wątki (streszczenie)

Programowanie obiektowe

Przygotował: Jacek Sroka. PO* - Scala (typy uogólnione, listy)

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Programowanie w Sieci Internet. Python: Wątki. Kraków, 12 grudnia 2014 r. mgr Piotr Rytko Wydział Matematyki i Informatyki

Kurs programowania. Wykład 9. Wojciech Macyna

Język programowania Scala + aktorzy Akka

Kurs programowania. Wykład 8. Wojciech Macyna. 10 maj 2017

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Wstęp do Programowania potok funkcyjny

WSPÓŁBIEŻNOŚĆ. MATERIAŁY:

Bloki anonimowe w PL/SQL

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Scala. Wprowadzenie do języka.

Scala - programowanie obiektowo-funkcyjne

Podstawy otwartych języków programowania Przechowywanie danych

Programowanie równoległe i rozproszone. W1. Wielowątkowość. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Wstęp do programowania

Platformy Programistyczne Podstawy języka Java

Pętle. for, while, do... while, foreach. Materiał pomocniczy do kursu Podstawy programowania Autor: Grzegorz Góralski ggoralski.

Kurs programowania. Wykład 8. Wojciech Macyna

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

1. Co można powiedzieć o poniższym kodzie (zakładając, że zaimportowano wszystkie niezbędne klasy)?

Java. Programowanie Obiektowe Mateusz Cicheński

Scala. Obiektowo-funkcyjny język programowania. Zbyszek Skowron

Współbieżność w Javie

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

Kurs programowania. Wykład 13. Wojciech Macyna. 14 czerwiec 2017

PHP może zostać rozszerzony o mechanizmy dostępu do różnych baz danych:

Instrukcja implementacji sterownika wirtualnego portu szeregowego dla systemu Android. Opracowanie: Elzab Soft sp. z o.o.

Programowanie obiektowe

Monitory. Jarosław Kuchta


Dawid Gierszewski Adam Hanasko

PO* - Scala (typy uogólnione, listy)

Wstęp. Ale po co? Implementacja

PHP: bloki kodu, tablice, obiekty i formularze

Tworzenie aplikacji w języku Java

Język Java wątki (streszczenie)

Mechanizmy komunikacji. spotkania symetryczne (język CSP) spotkania asymetryczne (Ada) przestrzenie krotek (Linda) potoki, komunikaty i kanały (Unix)

Podstawy programowania III WYKŁAD 6

w PL/SQL bloki nazwane to: funkcje, procedury, pakiety, wyzwalacze

Programowanie RAD Delphi

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Programowanie Komputerów

Podstawy współbieżności

Lista, Stos, Kolejka, Tablica Asocjacyjna

Języki skryptowe w programie Plans

Systemy operacyjne. Zajęcia 11. Monitory

Wprowadzenie db4o - podstawy db4o - technikalia Przydatne wiadomości. Wprowadzenie. db4o. Norbert Potocki. 1 czerwca Norbert Potocki db4o

Języki programowania wysokiego poziomu. PHP cz.2.

Klasy abstrakcyjne, interfejsy i polimorfizm

Współbieżność w Javie

Generatory. Michał R. Przybyłek

Programowanie obiektowe

Klasy i obiekty cz II

Model pamięci. Rafał Skinderowicz

Wzorce logiki dziedziny

Języki Programowania II Wykład 3. Java podstawy. Przypomnienie

Aplikacje Internetowe. Najprostsza aplikacja. Komponenty Javy. Podstawy języka Java

DIAGRAMY SYNTAKTYCZNE JĘZYKA TURBO PASCAL 6.0

Języki formalne i techniki translacji

Detekcja zakleszczenia (1)

Ćwiczenia 2 IBM DB2 Data Studio

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Programowanie i projektowanie obiektowe

Kolekcje w Javie cz. 1

Aplikacje WWW - laboratorium

Systemy Rozproszone. Spis treści. Temat projektu: Regułowy system analizujacy logi. autorzy: Rafał Sadłowski, Sebastian Falkus, Michał Różycki

KOTLIN. Język programowania dla Androida

Laboratorium Programowania Kart Elektronicznych

Bazy danych dla producenta mebli tapicerowanych. Bartosz Janiak Marcin Sikora Wrocław r.

Programowanie obiektowe

Programowanie współbieżne Laboratorium nr 11

Programowanie strukturalne. Opis ogólny programu w Turbo Pascalu

Programowanie współbieżne Wykład 5. Rafał Skinderowicz

Wykład 2 Wybrane konstrukcje obiektowych języków programowania (1)

Programowanie obiektowe

Tworzenie stron internetowych z wykorzystaniem HTM5, JavaScript, CSS3 i jquery. Łukasz Bartczuk

Db4o obiektowa baza danych wersja.net

Wykład 5. Synchronizacja (część II) Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Tablice cz. I Tablice jednowymiarowe, proste operacje na tablicach

Wątki w Javie. Piotr Tokarski

Transkrypt:

1 Scala + NetBeans AKA: Nowoczesne obiektowe języki programowania i środowiska programistyczne na przykładzie Scali i środowiska NetBeans (spotkanie 6)

2 Iteratory Iteratory to imperatywne wersje strumieni Po danych poruszamy się metodami hasnext() i next() (może nie być struktury danych) trait Iterator[+A] { def hasnext: Boolean def next: A... Przykład val it: Iterator[Int] = Iterator.range(1, 100) while (it.hasnext) { val x = it.next println(x * x)

3 Przydatne metody iteratorów Iteratory posiadają wiele metod wzorowanych na listach append def append[b >: A](that: Iterator[B]): Iterator[B] = new Iterator[B] { def hasnext = Iterator.this.hasNext that.hasnext def next = if (Iterator.this.hasNext) Iterator.this.next else that.next Czemu służy składnia Iterator.this.hasNext? map def map[b](f: A => B): Iterator[B] = new Iterator[B] { def hasnext = Iterator.this.hasNext def next = f(iterator.this.next)

4 Przydatne metody iteratorów c.d. flatmap def flatmap[b](f: A => Iterator[B]): Iterator[B] = new Iterator[B] { private var cur: Iterator[B] = Iterator.empty def hasnext: Boolean = if (cur.hasnext) true else if (Iterator.this.hasNext) {cur = f(iterator.this.next); hasnext else false def next: B = if (cur.hasnext) cur.next else if (Iterator.this.hasNext) { cur = f(iterator.this.next); next else error("next on empty iterator") foreach def foreach(f: A => Unit): Unit = while (hasnext) { f(next)

5 Przydatne metody iteratorów c.d. filter def filter(p: A => Boolean) = new BufferedIterator[a] { private val source = Iterator.this.buffered private def skip { while (source.hasnext &&!p(source.head)) { source.next def hasnext: Boolean = { skip; source.hasnext def next: A = { skip; source.next def head: A = { skip; source.head gdzie: trait BufferedIterator[+A] extends Iterator[A] { def head: A Skoro jest map, flatmap, filter i foreach to są też for-comprehensions i for-loops for (i <- Iterator.range(1, 100)) println(i * i)

6 Przydatne metody iteratorów c.d. zip def zip[b](that: Iterator[B]) = new Iterator[(a, b)] { def hasnext = Iterator.this.hasNext && that.hasnext def next = {Iterator.this.next, that.next

7 Konstruowanie iteratorów Najprostszy iterator to Iterator.empty object empty extends Iterator[Nothing] { def hasnext = false def next = error("next on empty iterator") Iterator na tablicy def fromarray[a](xs: Array[A]) = new Iterator[A] { private var i = 0 def hasnext: Boolean = i < xs.length def next: A = if (i < xs.length) { val x = xs(i); i += 1; x else error("next on empty iterator")

8 Konstruowanie iteratorów c.d. Iterator na przedziale Iterator.range def range(start: Int, end: Int) = new Iterator[Int] { private var current = start def hasnext = current < end def next = { val r = current if (current < end) current += 1 else error("end of iterator") r Nieskończony ciąg licz całkowitych (w praktyce iterujący w kółko po reprezentacji int) def from(start: Int) = new Iterator[Int] { private var last = start - 1 def hasnext = true def next = { last += 1; last

9 Przykład zastosowań Wypisywanie tablicy xs: Array[Int] Iterator.fromArray(xs) foreach (x => println(x)) for (x <- Iterator.fromArray(xs)) println(x) Znajdywanie indeksów elementów tablicy o wartości większej niż zadana stała import Iterator._ fromarray(xs).zip(from(0)).filter(case (x, i) => x > limit).map(case (x, i) => i)?

10 Przykład zastosowań Wypisywanie tablicy xs: Array[Int] Iterator.fromArray(xs) foreach (x => println(x)) for (x <- Iterator.fromArray(xs)) println(x) Znajdywanie indeksów elementów tablicy o wartości większej niż zadana stała import Iterator._ fromarray(xs).zip(from(0)).filter(case (x, i) => x > limit).map(case (x, i) => i) import Iterator._ for ((x, i) <- fromarray(xs) zip from(0); if x > limit) yield i

11 Wbudowana leniwość Przykład: obsługa bazy pracowników, gdzie każdy pracownik ma szefa i zespół. Z gorliwą inicjalizacją większość danych jest od razu wczytywana do pamięci case class Employee(id: Int, name: String, managerid: Int) { val manager: Employee = Db.get(managerId) val team: List[Employee] = Db.team(id) Z leniwą inicjalizacją nie case class Employee(id: Int, name: String, managerid: Int) { lazy val manager: Employee = Db.get(managerId) lazy val team: List[Employee] = Db.team(id)

12 Przykład c.d. Poniższa implementacja bazy informuje kiedy wczytywane są dane. object Db { val table = Map(1 -> (1, "Haruki Murakami", 1), 2 -> (2, "Milan Kundera", 1), 3 -> (3, "Jeffrey Eugenides", 1), 4 -> (4, "Mario Vargas Llosa", 1), 5 -> (5, "Julian Barnes", 2)) def team(id: Int) = { for (rec <table.values.tolist; if rec._3 == id) yield rectoemployee(rec) def get(id: Int) = rectoemployee(table(id)) private def rectoemployee(rec: (Int, String, Int)) = { println("[db] fetching " + rec._1) Employee(rec._1, rec._2, rec._3)

13 Leniwość a cykliczne zależności Bez leniwości rekursja przy inicjalizacji wartości lokalnych jest niedozwolona class Symbols(val compiler: Compiler) { import compiler.types._ val Add = new Symbol("+", FunType(List(IntType, IntType), IntType)) val Sub = new Symbol("-", FunType(List(IntType, IntType), IntType)) class Symbol(name: String, tpe: Type) { override def tostring = name + ": " + tpe class Types(val compiler: Compiler) { import compiler.symtab._ abstract class Type case class FunType(args: List[Type], res: Type) extends Type case class NamedType(sym: Symbol) extends Type case object IntType extends Type

14 Leniwość a cykliczne zależności c.d. Tu będzie błąd w chwili wykonania class Compiler { val symtab = new Symbols(this) val types = new Types(this) A tu nie (zostanie wybrana właściwa kolejność) class Compiler { lazy val symtab = new Symbols(this) lazy val types = new Types(this)

15 Implicit parameters Półgrupa abstract class SemiGroup[A] { def add(x: A, y: A): A Monoid abstract class Monoid[A] extends SemiGroup[A] { def unit: A Implementacje object stringmonoid extends Monoid[String] { def add(x: String, y: String): String = x.concat(y) def unit: String = "" object intmonoid extends Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0

16 Implicit parameters Funkcja sumująca listy wartości z dowolnego monoidu def sum[a](xs: List[A])(m: Monoid[A]): A = if (xs.isempty) m.unit else m.add(xs.head, sum(xs.tail)(m)) sum(list("a", "bc", "def"))(stringmonoid) sum(list(1, 2, 3))(intMonoid) Co zrobić żeby drugi parametr był odgadywany tak samo jak zazwyczaj kompilator odgaduje typ?

17 Implicite parameters Ostatnia lista parametrów może być wyróżniona słowem implicit def sum[a](xs: List[A])(implicit m: Monoid[A]): A = if (xs.isempty) m.unit else m.add(xs.head, sum(xs.tail)) Takim samym słowem trzeba też wyróżnić obiekty oraz deklaracje implicit object stringmonoid extends Monoid[String] { def add(x: String, y: String): String = x.concat(y) def unit: String = "" implicit object intmonoid extends Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0 Scala wybierze najbardziej pasującą wartość z miejsca wywołania metody.

18 Konwersje Jeżeli wyrażenie zwraca wartość innego typu niż spodziewany Scala spróbuje skorzystać z dostępnych konwersji implicit def int2ordered(x: Int): Ordered[Int] = new Ordered[Int] { def compare(y: Int): Int = if (x < y) -1 else if (x > y) 1 else 0

19 View Bounds Można parametryzować typami, dla których istnieją wymagane konwersje. def sort[a <% Ordered[A]](xs: List[A]): List[A] = if (xs.isempty xs.tail.isempty) xs else { val {ys, zs = xs.splitat(xs.length / 2) merge(ys, zs) To lukier dla (żądamy konwersji) def sort[a](xs: List[A])(implicit c: A => Ordered[A]): List[A] =...

20 View Bounds c.d. Kontynuująć def merge[a <% Ordered[A]](xs: List[A], ys: List[A]): List[A] = if (xs.isempty) ys else if (ys.isempty) xs else if (xs.head < ys.head) xs.head :: merge(xs.tail, ys) else if ys.head :: merge(xs, ys.tail) To lukier dla def merge[a](xs: List[A], ys: List[A])(implicit c: A => Ordered[A]): List[A] = if (xs.isempty) ys else if (ys.isempty) xs else if (c(xs.head) < ys.head) xs.head :: merge(xs.tail, ys) else if ys.head :: merge(xs, ys.tail)(c)

21 Współbieżność

Sygnały i monitory Każdy egzemplarz AnyRef może być użyty jako monitor def synchronized[a] (e: => A): A def wait() def wait(msec: Long) def notify() def notifyall() Nie ma gwarancji, że obudzony wątek natychmiast się wykona (trzeba w pętli sprawdzić czy zaszły warunki do kontynuowania) class BoundedBuffer[A](N: Int) { var in = 0, out = 0, n = 0, elems = new Array[A](N) def put(x: A) = synchronized { while (n >= N) wait() elems(in) = x ; in = (in + 1) % N ; n = n + 1 if (n == 1) notifyall() def get: A = synchronized { while (n == 0) wait() val x = elems(out) ; out = (out + 1) % N ; n = n - 1 if (n == N - 1) notifyall() x 22

23 Sygnały i monitory c.d. Przykład użycia import scala.concurrent.ops._... val buf = new BoundedBuffer[String](10) spawn { while (true) { val s = producestring ; buf.put(s) spawn { while (true) { val s = buf.get ; consumestring(s) gdzie spawn jest zdefiniowane w scala.concurrent.ops def spawn(p: => Unit) { val t = new Thread() { override def run() = p t.start()

SyncVars Zmienne z get, set i unset (get blokuje dopóki nie ma wartości) package scala.concurrent class SyncVar[A] { private var isdefined: Boolean = false private var value: A = _ def get = synchronized { while (!isdefined) wait() value def set(x: A) = synchronized { value = x; isdefined = true; notifyall() def isset: Boolean = synchronized { isdefined def unset = synchronized { isdefined = false 24

25 Futures Wartość wyliczane współbieżnie, żeby w przyszłości zostać użyte Zastosowanie import scala.concurrent.ops._... val x = future(somelengthycomputation) anotherlengthycomputation val y = f(x()) + g(x()) Definicja w scala.concurrent.ops def future[a](p: => A): Unit => A = { val result = new SyncVar[A] fork { result.set(p) (() => result.get)

26 Inicjowanie współbieżności Współbieżne wykonanie pary obliczeń i zwrócenie pary wyników def par[a, B](xp: => A, yp: => B): (A, B) = { val y = new SyncVar[B] spawn { y set yp (xp, y.get) Można wykonać wiele obliczeń, każde wyróżnione numerkiem def replicate(start: Int, end: Int)(p: Int => Unit) { if (start == end) () else if (start + 1 == end) p(start) else { val mid = (start + end) / 2 spawn { replicate(start, mid)(p) replicate(mid, end)(p) Zastosowanie: przetwarzanie wszystkich elementów tablicy def parmap[a,b](f: A => B, xs: Array[A]): Array[B] = { val results = new Array[B](xs.length) replicate(0, xs.length) { i => results(i) = f(xs(i)) results

27 Semafory package scala.concurrent class Lock { var available = true def acquire = synchronized { while (!available) wait() available = false def release = synchronized { available = true notify()

28 Kanały asynchroniczne class Channel[A] { class LinkedList[A] { var elem: A = _ var next: LinkedList[A] = null private var written = new LinkedList[A] //czubek listy z którego czytamy private var lastwritten = written //pusty początek listy przez który dopisujemy private var nreaders = 0 def write(x: A) = synchronized { lastwritten.elem = x lastwritten.next = new LinkedList[A] lastwritten = lastwritten.next if (nreaders > 0) notify() def read: A = synchronized { while (written.next == null) { nreaders = nreaders + 1; wait(); nreaders = nreaders - 1 val x = written.elem written = written.next x

29 Kanały synchroniczne class SyncChannel[A] { private var data: A = _ private var reading = false private var writing = false def write(x: A) = synchronized { while (writing) wait() data = x writing = true if (reading) notifyall() else while (!reading) wait() def read: A = synchronized { while (reading) wait() reading = true while (!writing) wait() val x = data writing = false reading = false notifyall() x

30 Serwer obliczeniowy Przy pomocy kanału można zaimplementować serwer obliczeniowy z odgórnie ustaloną liczbą workerów (np. tyle co mamy rdzeni) class ComputeServer(n: Int) { private abstract class Job { type T def task: T //obliczenie do wykonania def ret(x: T) //metoda zwracająca wyni private val openjobs = new Channel[Job]() private def processor(i: Int) { while (true) { val job = openjobs.read job.ret(job.task)...

31 Serwer obliczeniowy c.d.... def future[a](p: => A): () => A = { val reply = new SyncVar[A]() openjobs.write{ new Job { type T = A def task = p def ret(x: A) = reply.set(x) () => reply.get spawn(replicate(0, n) { processor )

32 Serwer obliczeniowy c.d. Przykład użycia: object Test with Executable { val server = new ComputeServer(1) val f = server.future(41 + 1) println(f())

33 Mailbox Sygnatura class MailBox { def send(msg: Any) def receive[a](f: PartialFunction[Any, A]): A def receivewithin[a](msec: Long)(f: PartialFunction[Any, A]): A Funkcja częściowa przekazywana jako parametr jest wykonywana jeżeli jest pasująca wiadomość, wpp. jest wstrzymywana dopóki ta wiadomość się nie pojawi Wiadomościami mogą być dowolne obiekty jest wyróżniona wiadomość case object TIMEOUT

34 Mailbox: przykład użycia Bufor z miejscem na jedną wartość class OnePlaceBuffer { private val m = new MailBox // An internal mailbox private case class Empty, Full(x: Int) // Types of messages we deal with m send Empty // Initialization def write(x: Int) { m receive { case Empty => m send Full(x) def read: Int = m receive { case Full(x) => m send Empty; x

35 Mailbox: przykładowa implementacja Receiver reprezentuje obsługę komunikatu, msg jest ustawiane jak dopasowano pasujący komunikat private abstract class Receiver extends Signal { def isdefined(msg: Any): Boolean var msg = null Pamiętamy listy czekających wiadomości i Receiverów private val sent = new LinkedList[Any] private var lastsent = sent private val receivers = new LinkedList[Receiver] private var lastreceiver = receivers

36 Mailbox: przykładowa implementacja c.d. Przy wysyłaniu sprawdzamy czy jest pasujący Receiver i jeżeli tak to go wzbudzamy def send(msg: Any) = synchronized { var r = receivers, r1 = r.next while (r1!= null &&!r1.elem.isdefined(msg)) { r = r1; r1 = r1.next if (r1!= null) { r.next = r1.next; r1.elem.msg = msg; r1.elem.notify else { lastsent = insert(lastsent, msg)

37 Mailbox: przykładowa implementacja c.d. Przy pobieraniu najpierw sprawdzamy czy któraś z przechowywanych wiadomości pasuje do funkcji, wpp. tworzony jest nowy Receiver i zapamiętywany na liście i wątek zasypia aż do jego wzbudzenia. Po obudzeniu wątek odpala f dla wiadomości zapamiętanej na msg def receive[a](f: PartialFunction[Any, A]): A = { val msg: Any = synchronized { var s = sent, s1 = s.next while (s1!= null &&!f.isdefinedat(s1.elem)) { s = s1; s1 = s1.next if (s1!= null) { s.next = s1.next; s1.elem else { val r = insert(lastreceiver, new Receiver { def isdefined(msg: Any) = f.isdefinedat(msg) ) lastreceiver = r r.elem.wait() r.elem.msg f(msg)

Mailbox: zastosowanie class ReadersWriters { val m = new MailBox private case class Writers(n: Int) { m send this private case class Readers(n: Int) { m send this Writers(0) Readers(0) def startread = m receive { case Writers(0) => m receive { case Readers(n) => Readers(n+1); Writers(0) def startwrite = m receive { case Writers(0) => m receive { case Readers(0) => Writers(1) def endread = m receive { case Readers(n) => Readers(n-1) def endwrite = m receive { case Writers(1) => Writers(0); Readers(0) 38

39 Aktorzy Esencją aktora jest wątek posiadający mailbox Przykład z aukcją (ze skryptu) będzie na labie

40 Lift

41 Najważniejsze cechy security, maintainability, scalability, performance productivity Zaczerpnięcie dobrych pomysłów z istniejących rozwiązań Seaside wysoce ziarnista sesja i bezpieczeństwo Rails szybko widoczne efekty pracy Django gotowe CRUD i jeszcze trochę Wicket wygodne dla projektanta podejście do wzorców (Lift View First) aplikacje są pakowane jako WAR i osadzane na kontenerach Servlet 2.4 działają na Tomccie 5.5.xx oraz Jetty 6.0