wtorek, 30 sierpnia 2011

Zaczyn DDD+CqRS

Jak pewnie zauważyliście, częstotliwość publikowania postów spadła ostatnio dosyć znacznie. A to za sprawą mocnego zaangażowania w nowy projekt - projekt ilustrujący techniki modelowania Domain Driven Design oraz wybrane podejścia do implementacji architektury Command-query Responsibility Segreation.

Projekt osiągnął poziom wersji Beta pierwszego Milestonea, tak więc już oficjalnie możemy go upublicznić: http://code.google.com/p/ddd-cqrs-sample/.



Zaczyn?
Nazwa ociekająca folklorem jest ilustracją idei: projekt to coś więcej niż Sample ale zdecydowanie nie jest to kolejny Framework - więcej na ten temat na wiki.
Gwoli wyjaśnienia: zaczyn to coś z czego powstaje chleb.

Odeszliśmy od formy frameworka przekonani przez samego Grega Younga (twórcę CqRS i propagatora DDD). Greg przekonał mnie podczas prywatnych rozmów, że framework dla CqRS delikatnie mówiąc nie ma sensu:)



Zawartość projektu odpowiada na pytania techniczne, które pojawiają się tak często, że uznaliśmy, że czas stworzyć sampla ilustrującego nasze podejście. Do tej pory istnieją dwa takie Sample napisane w Javie - my jednak chcieliśmy pójść nieco dalej. Tak więc projekt jest sumą doświadczeń jego twórców.

Co w nim znajdziecie:
- ilustracja wszystkich Builiding Blocków DDD (bez uproszczeń technicznych)
- ilustracja zaawansowany technik DDD (Bounded Context to nie są akademickie rozważania)
- ewolucyjne podejście do architektury warstwowej, różne poziomy separacji CqRS (na tym etapie nie wprowadzamy Event Sourcingu - wyjaśnienie powodów na wiki)
- pragmatyczne podejście do CqRS
- część bardzo specyficznej wiedzy wyniesioną przez nas z wiosennego szkolenia Grega Younga
- modelowanie czasu poprzez zdarzenia i dosyć nową technikę modelowania czasu: Sagi
- kilka eleganckich trików z wykorzystaniem Springa i Hibernate:)
- techniki testowania: jednostkowe, integracyjne i akceptacyjne
- połączenie JBehave (Behavior Driven Development) z Selenium
- już niedługo: nasz własny pomysł na uogólnienie historyjek tak by wspierały zarówno testowanie przez GUI jak i przez API



Zainteresowanych zapraszamy do:
- wiki - kilkadziesiąt stron A4 plus trzy wizualizacje w Prezi
- SVN - aktualnie ok 150 klas platformy zaczynu oraz przykładów DDD
- grupy dyskusyjnej
- bloga


//====================================

Teksty techniczne z zakresu DDD i CqRS będę publikował na nowym blogu. Natomiast niniejszy będzie wciąż prowadzony jako miejsce na tematy pozostałe.

piątek, 12 sierpnia 2011

Wygeneruj sobie PDFa - Facelets (JSF) + CSS3 + Flying Saucer

Dzisiejszy post będzie nietypowy: dużo kodu i kilka speszyl-haków:)

Pokażę jak szybko i raczej bezboleśnie generować PDFy na podstawie HTMLa + CSS3 z wykorzystaniem szablonów w wydaniu Facelets.

Założenia

Oczywiście do generowania PDF możemy podejść na wiele sposobów. Możemy np. użyć natywnego api lub nawet zastosować Open Office postawionego w trybie servera (bez GUI) i korzystać ze wszelkich narzędzi do tworzenia dokumentu, aby na końcu "zapisać jako" pdf.

Natomiast w tym poście zakładam, że chcemy rozwiązać sprawę najprościej jak się da, bez potrzeby studiowania autystycznej dokumentacji.

Generujemy PDF

Na początek potrzebujemy biblioteki: http://code.google.com/p/flying-saucer (w paczce mamy ITexta i xercesa). Flying saucer to wspaniałe narzędzie, które przykrywa iTexta wygodnym api: na wejściu podajemy urla do strony, a na strumieniu wyjściowym dostajemy PDFa. Strona może odwoływać się do CSS3, w którym definiujemy nagłówki, stopki, numerowanie stron, podział stron. Wszystko zostanie kolejno opisane w dalszej części.

Pdfy będziemy zwracać z następującego servletu:
@SuppressWarnings("serial")
@WebServlet("/programy_szkolen_pdf/*")
public class PdfServlet extends HttpServlet {
	
	private static final String FONTS_PACKAGE = "fonts";
	private static final String FONTS_DIR = "/" + FONTS_PACKAGE + "/";
	
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {				
		
		response.setContentType("application/pdf");
		OutputStream os = response.getOutputStream();
		
		String url = getUrl(request);

		try {
			ITextRenderer renderer = initRenderer();
			
			renderer.setDocument(url);
			renderer.layout();
			
			renderer.createPDF(os);
		} catch (Exception ex){
			throw new ServletException(ex);
		}
		
		response.flushBuffer();

	}

	private String getUrl(HttpServletRequest request) {
		String pdfServletPath = request.getServletPath();
		
		String cp = request.getContextPath();
		String program = request.getRequestURI();
		
		program = program.replace(cp + pdfServletPath, "");
		program = program.substring(0, program.length() - 4); // cut .pdf
				
		String url = "http://localhost:8080" + cp + program + ".xhtm?pdf=true";
		
		return url;
	}
}

Jak widać API latającego spodka jest proste:
- posługujemy się obiektem rendererm który posiada stan
- ustawiamy mu URL do strony, którą chcemy skonwertować na PDF
- wołamy metodę układającą pdf
- wynik kierujemy do strumienia odpowiedzi servletu.

I to generalnie byłoby wszystko:)

Zostało kilka szczegółów...
Metoda getUrl w moim przypadku robi pewne oszustwo operując na ścieżkach

Użytkownik woła servlet:
/programy_szkolen_pdf/szkolenie-java-start.pdf
btw: zauważcie jak zamapowany jest servlet: @WebServlet("/programy_szkolen_pdf/*")

A servlet wskazuje latającemu spodkowi adres:
http://localhost:8080/strona/szkolenie-java-start.xhtm?pdf=true

Zakładam, że zasoby do renderowania znajdują się na tej samej maszynie, stąd localhost.
Do czego służy parametr pdf dowiemy się za chwilę:)


Mamy jeszcze nieco smutnych szczegółów technicznych w naszym latającym spodku.

Renderera trzeba zainicjować - metoda initRenderer.

Jeżeli w PDF chcemy używać polskich (lub jakichkolwiek innych barbarzyńskich znaków), to musimy zarejestrować w rendereze fonty, których używamy w CSS (Arial, Verdana, cokolwiek). Służy do tego metoda renderer.getFontResolver().addFont
Ja napisałem prosty automat, metodę findFonts(), który automatycznie wyszukuje plików z fontami w *pakiecie* zdefiniowanym w stałej FONTS_PACKAGE. Chodzi o to aby uniknąć konfigurowania ścieżek. Po prostu skanujemy pakiet z class path.
Zatem do odpowiedniego pakietu należy sobie wkopiować (np z systemu) fonty. Oczywiście katalog powinien być niedostępny z sieci aby nie być posądzonym o dystrybucję komercyjnych fontów - jeżeli takowych używacie.

	
	private ITextRenderer initRenderer() throws IOException, DocumentException{
		List fonts = findFonts();
		
		ITextRenderer renderer = new ITextRenderer();
		
		for (String font : fonts) {
			renderer.getFontResolver().addFont(FONTS_DIR + font,
					BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
		}			
		
		return renderer;
	}

	private List findFonts() throws IOException {
		Enumeration resources = getClass().getClassLoader().getResources(FONTS_PACKAGE);

		URL resource = resources.nextElement();
		File directory = new File(resource.getFile());

		List fonts = new ArrayList();
		if (!directory.exists()) {
			return fonts;
		}
		File[] files = directory.listFiles();
		for (File file : files) {			
			fonts.add(file.getName());
		}
		return fonts;
	}


Szablony

Dlaczego Facelets i JSF 2?

Do tej pory różnego rodzaju strony tworzyłem w PHP z wykorzystaniem Smarty. Po kilku doświadczeniach uznałem to za... powiedzmy niewygodne.

Chcę być dobrze zrozumiany - Smarty są ok dla pewnej klasy problemów: np wówczas gdy mamy kontent w bazie danych (jakiś CMS), pobieramy kontent jako wiersze i rozsmarowujemy po po "dziurkach" w szablonie. Wówczas imperatywne API Smarty (w postaci obiektu, na którym wołamy metody definiujące zmienne szablonu) jest jak najbardziej ok.

Natomiast ja w klasach problemów typu strona firmowa, preferuję przechowywać kontent (strzępy konwentu) w plikach XHTML. W takim wypadku praca z imperatywnym API to po prostu pomyłka. Jako "weteran" aplikacji webowych czuję się jak ryba w wodzie poruszając się w kodzie. Baza i CMS jedynie mnie ogranicza.

Dlatego tworząc nową wersję strony formowej zdecydowałem się na coś... delikatnie mówiąc... zastawiającego. Użycie JSF - standardu prezentacji enterprise (która ma swoich fanów jak i anty-fantów) do stworzenia strony www :O

Okazało się, że jeżeli nie używać formularzy (pomijamy większość faz cyklu obsługi żądania), nie używać ManagedBeanów, a jedynie składania kawałków XHTMla to okazuje się, bo jest to bardzo produktywne narzędzie.

Przykład:
strona http://bottega.com.pl/szkolenie-jsf-2
wygląda jak każdy widzi.

Jest wyraźnie podzielona na kontent merytoryczny i ozdobniki w postaci nagłówka, stopki i paneli bocznych.

A tak wygląda jej kod:

	
	
		
			
	

	Java Enterprise Edition

	JSF 2
	
		Java Server Faces 2

	Projektanci, programiści

	2 dni

	50% wykłady / 50% warsztaty

			
	
		

Program został skontrowany tak aby przedstawić zagadnienia w kontekście konkretnych problemów, które ilustrują praktyczne wykorzystanie każdej z funkcjonalności JSF. Program szkolenia zawiera rozszerzenie o najlepsze praktyki projektowe i architektoniczne. Tematyka szkolenia obejmuje wszystkie cechy frameworka JSF 2.0 istotne z punktu widzenia developera aplikacji.


Znaczniki ui:definie definiują wartości zmiennych, które będą używane w szablonie.
Wartości to czysty kod XHTML, który jest na bieżąco validowany przez edytor.

Stronę możemy zawołać na 3 sposoby:
- podając jej url: http://bottega.com.pl/szkolenie-jsf-2
- renderując ją do pdf: http://bottega.com.pl/programy_szkolen_pdf/szkolenie-jsf-2.pdf (wówczas nasz servlet przekaże adres http://bottega.com.pl/szkolenie-jsf-2.xhtm?pdf=true) do Flying Saucera
- zagnieżdżając w pływającej ramce na facebooku podając adres http://bottega.com.pl/szkolenie-jsf-2.xhtm?fb=true

W każdym z tych przypadków oczekujemy innego wyglądu.

Zatem znacznik ui:composition wskazuje szablon: template="/layout/training_template.xhtml"

Plik ten układa w odpowiednich miejscach parametry szkolenia. Szablon ten odpowiada jedynie za fragment strony (układ info o szkoleniu), ale nie zawiera np. nagłówka, stopki, ani bocznych boxów.



		Szkolenie: 
		

	
		

Informacje ogólne


Jak widać, szablon ten definiuje 2 zmienne: title i body. Wartości tych zmiennych zawierają w sobie XHTMLa oraz wartości zmiennych z dekorowane pliku - znacznik ui:insert.

Warunkowe renderowanie

Pomarańczowy przycisk "Zapytaj o szkolenie" jest niepożądany w przypadku wersji PDF i facebook, dlatego został warunkowo renderowany, przy pomocy panelGroup i atrybutu rendered:



Bean UrlChecker będzie opisany już za chwilę.


Wybór szablonu docelowego

Szablon szkolenia, nie jest szablonem "ostatecznym". Jest on dekorowane znowuż przez szablon wybierający szablon główny: template="/layout/dispatching_template.xhtml"

Jest to warstwa pośrednia pozwalająca w jednym miejscu przechowywać kod, który potencjalnie ulegnie zmianie. Tak oto wygląda dispatching_template.xhtml:


	


Jak widać nie dodaje żądnej wizualizacji a jedynie deleguje dekorowanie do kolejnego szablonu. Z tym, że nie mamy tutaj konkretnej ścieżki a jedynie wyrażenie wyliczane w Javie (tak w normalnym języku programowania a nie w jakimś pokracznym języku opartym o znaczniki):

@ManagedBean
public class UrlChecker {

	public String getTemplateName(){
		if (isPdf())
			return "template_pdf";
		
		if (isFacebook())
			return "template_facebook";
		
		return "template";
	}
	
	public boolean isStandard(){
		return ! (isPdf() || isFacebook());
	}
	
	public boolean isPdf(){		
		return "true".equals(getParameter("pdf"));
	}
	
	public boolean isFacebook(){		
		return "true".equals(getParameter("fb"));
	}
	
	private String getParameter(String name){
		ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();				
		return externalContext.getRequestParameterMap().get(name);		
	}
	
	
}

Zatem jeżeli zawołamy naszą stronę w ten sposób:
http://bottega.com.pl/szkolenie-jsf-2.xhtm
to w wyniku otrzymamy zawartość dekorowaną szablonem template_pl, który zawiera banner, stopę i boczne boxy

jeżeli natomiast w ten sposób:
http://bottega.com.pl/szkolenie-jsf-2.xhtm?pdf=true
to szablon dekorujący template_pdf_pl doda nagłówek i stopkę dla dokumnetu

a takie wywołanie
http://bottega.com.pl/szkolenie-jsf-2.xhtm?fb=true skutkuje zastosowaniem niemal niewidocznego szablonu dla facebooka.


Podsumowanie dekorowania


Mamy zatem następującą hierarchię treści:

szablon: szablon www | szablon pdf | szablon facebook
szablon: dispatcher
szablon: training
strona: konkretne szkolenie

Czyli na najwyższym poziomie mamy trzy perspektywy (szablony) spojrzenia na dane merytoryczne zawarte w plikach opisujących szkolenie.

Inne strony nie będące programem szkoleń po prostu pomijają szablon training_template i są dekorowane wprost szablonem dispatcher template


Układ dokumentu PDF w HTML i CSS3

Składanie stron w HTML jest dosyć łatwe. Wręcz banalne w porównaniu z natywnym API lub autyzmem Open Office.

Szablon pdf to zwykły XHTML:
<head>


</head>

<body>
  
!!! TUTAJ trzy warstwy opisane poniżej !!!
<</body>

Style po komentarzy "Specific Elements" są *kluczowe*.


Wewnątrz pierwszej warstwy mamy kolejno kolejno:

nagłówek:


stopkę:


zawartość:

Program szkolenia




Na zakończenie bonus dla wytrwałych

Jak wołać w JSF 2 metody z parametrem? Zapewniam, że może się przydać w bardziej wyrafinowanych szablonach:)

Tak robimy to w Seam od 5 lat:




Natomiast w czystym JSF trzeba wykonać kilka prostych kroków aby uzyskać możliwość stosowania powyższej składni:
http://ocpsoft.com/java/jsf2-java/jsf2-how-to-add-the-magic-of-el-el2-to-jsf/

//====================================

Mam nadzieję, że udało mi się pokazać, że w Javie można efektywnie a przy tym czysto i elegancko zbudować stronę.

Przy innej okazji pokażę jak Filtry Servletów w dosłownie kilku linijkach biją na głowę autystyczne htaccess:)

A Smarki?... Już nigdy więcej:)