W metodzie configureAuth uzyskuję dostęp do obiektu AuthenticationManagerBuilder i definiuję parę użytkowników i nadajemy im role (użytkownika lub administratora).
W metodzie configure podaję, kto ma mieć dostęp do której części aplikacji. I tak:
Do adresów wymienionych w linii 17 dostęp mają wszyscy, a więc nie tylko zalogowani użytkownicy.
(18) Do stron pod adresem /admin i poniżej dostęp ma tylko użytkownik o prawach administratora.
(19) Do stron pod adresem /user i poniżej dostęp ma tylko użytkownik w roli USER.
(20) Do reszty stron dostęp mają tylko zalogowani użytkownicy, niezależnie od roli.
Włączam dodatkowo standardowy formularz logowania (21) i ustawiam adres / jako domyślne miejsce lądowania po udanym zalogowaniu bądź wylogowaniu.
Dodam jeszcze następujący fragment kodu do mojego szablonu strony:
Uwaga! Żeby silnik szablonów Thymeleaf odpowiednio przetworzył ten fragment, w pom.xml muszę dodać zależność do artefaktu thymeleaf-extras-springsecurity4.
Każdy programista wie, że duplikacja w kodzie źródłowym to zło, gdyż:
jeśli zduplikowany kod zawiera błąd, trzeba naprawić go w kilku miejscach (i z reguły o którymś się zapomina),
długi kod trudniej się czyta.
A jednak zadanie przerzucenia powtarzanych fragmentów HTML (jak nagłówek i stopka wyświetlane na wszystkich podstronach) do osobnych, reużywalnych plików wcale nie jest trywialne!
Mój plik index.html w oryginalnej postaci zawierał 71 linii, z czego około 50 było wspólnych z innymi szablonami (nagłówek, stopka, menu, załączane skrypty js). Zależało mi na wyodrębnieniu części wspólnych do własnych plików. Chociażby po to, żeby dodanie pozycji z menu nie musiało być odwzorowywane w pięciu miejscach.
Jakie miałam możliwości?
Oprogramować operację include w JavaScripcie (lub użyć czyjegoś kodu, np. z W3Schools). Wady:
dużo JavaScriptu,
metoda z gatunku nieeleganckich hacków.
Użyć znaczników HTML:object, embed lub nawet iframe. Wady:
dwa pierwsze znaczniki zostały zaprojektowane do osadzania na stronach obiektach takich jak filmy, choć da się ich użyć tak że do wstawienia dokumentu HTML,
wszystkie służą do dodania do strony kompletnego dokumentu, a nie fragmentu,
styl CSS obowiązujący na stronie nie zadziała na załączone w ten sposób fragmenty.
Skorzystać z mechanizmu udostępnianego przez silnik szablonów, w tym wypadku Thymeleaf. Wady:
serwer wykonuje pracę, którą mógłby wykonać klient 🙂
ewentualna zmiana silnika szablonów staje się jeszcze bardziej kosztowna.
Zdecydowałam się na rozwiązanie numer 3. Jak to wygląda w praktyce?
Na wszystkich moich stronach powtarzał się poniższy fragment:
1
2
3
4
5
6
7
<!--Header-->
<header id="header"class="alt">
<h1>
<ahref="#">Szafbook</a>
</h1>
<ahref="#nav">Menu</a>
</header>
Przeniosłam go (wraz z innymi podobnymi elementami) do osobnego pliku _menus.html:
1
2
3
4
5
6
<header th:fragment="title-menu">
<h1>
<ahref="/">Szafbook</a>
</h1>
<ahref="#nav">Menu</a>
</header>
Dzięki temu mogę załączać go wszędzie tam, gdzie jest potrzebny, w następujący sposób:
Pułapka 1: Na początku umieściłam atrybuty id i class we fragmencie załączanym (w _menus.html). Zostały zjedzone 🙂
A co, jeśli fragment, który chcę załączyć, nie jest otoczony sensowną parą znaczników? Mogę wtedy użyć bloku, np.:
1
2
3
4
<th:block th:fragment="scripts">
<script src="assets/js/jquery.min.js"></script>
...
</th:block>
I załączyć go w analogiczny sposób:
1
<th:block th:include="_others :: scripts"/>
W obecnej chwili index.html ma tylko 32 niepuste linie i dużo zyskał na czytelności, podobnie jak inne szablony.
Pułapka 2: Po wyodrębnieniu stopki do osobnego pliku, moim oczom ukazał się taki oto widok:
Problem z kodowaniem załączonego pliku
Wszystkie pliki mam zakodowane w UTF-8, takie samo kodowanie deklaruję w nagłówku HTML. Poczytałam trochę o problemach z kodowaniem na linii Spring/Thymeleaf, ale ostatecznie, ponieważ na teraz jest to jedyne wystąpienie polskiego znaku na mojej stronie, po prostu zmieniłam odpowiadającą za to linię na:
Podobny obrazek widział chyba każdy programista aplikacji webowej. Od czasu do czasu serwer Tomcat postanawia poinformować użytkownika aplikacji, że coś poszło nie tak. Informuje dość brutalnie, przynajmniej od strony stylistycznej. Co gorsza, przy okazji potrafi wyjawić przypadkowemu użytkownikowi dość dużo informacji na temat wnętrza programu.
Informacja o błędzie wygenerowana przez serwer Tomcat. W tym wypadku żądana strona nie istnieje. Czasami można w tym miejscu zobaczyć ciekawszy materiał, czyli stack trace.
Tego typu informacje przydają się do debugowania aplikacji na etapie jej powstawania, ale użytkownik końcowy nie powinien ich oglądać (chociażby z powodów estetycznych).
Jak sobie z tym poradzić? Framework Spring Boot automatycznie ustawia dla nas nieco tylko ładniejszą stronę błędu:
Whitelabel Error Page, czyli strona błędu generowana przez Spring Boot
Mechanizm ten można wyłączyć we właściwościach aplikacji (application.properties):
1
server.error.whitelabel.enabled=false
Także zmiana domyślnego ViewResolver może usunąć tę wersję informacji o błędzie i przywrócić wersję tomcatową.
Jeśli zamiast strony prezentowanej przez Springa chcesz ustawić swoją własną, musisz zerknąć do dokumentacji wybranego przez Ciebie silnika szablonów. W przypadku Thymeleaf wystarczy dodać szablon o nazwie error.html:
Szablon błędu dla silnika Thymeleaf
Dzięki temu strona błędu może wyglądać na przykład tak:
Czy jest ładniejsza… Pozostawiam ocenie Czytelnika 😀
Ten tekst miał nazywać się “Mobilna wersja strony: podejście 2” w nawiązaniu do Mobilna wersja strony w Spring Boot. Potrzebowałam do niego zrzutu ekranu z mojego telefonu, a jedno spojrzenie na niego wyzwoliło całą lawinę wspomnień… Które również zamieszczam poniżej.
@media
Najpierw krótko o konkretach: w poprzednim wpisie pokazywałam, jak Spring Boot pozwala stworzyć mobilną wersję strony i zarządza jej wyświetlaniem. Wychodzi jednak na to, że w dzisiejszych czasach istnieją lepsze metody. Prym wśród nich wiodą CSS3 i reguły @media. Logika odpowiedzialna za formatowanie informacji trafia w tym modelu dokładnie tam, gdzie powinna: do arkusza stylów. W arkuszu stylów, przy użyciu reguł @media, możemy podjąć decyzję na temat sposobu wyświetlania informacji w zależności od tego, jakie urządzenie korzysta ze strony (np. ekran czy drukarka?), jaką ma orientację (pozioma czy pionowa?) bądź rozdzielczość.
Kolor tła zostanie zmieniony na magentę (ekhem, fuksję), jeśli szerokość wyświetlacza wyniesie co najmniej 480 pikseli.
W swojej konkursowej aplikacji postanowiłam pójść po najmniejszej linii oporu – znalazłam darmowy szablon CSS implementujący ten mechanizm 🙂 Wygląda to obecnie tak:
Przeglądarka na PCStrona wyświetlona na ekranie urządzenia mobilnego o niskiej rozdzielczości
Telefony 🙂
Co do kiepskiej jakości urządzeń mobilnych… Jestem specyficznym typem użytkownika: masowo rozbijam telefony. Nie rzucam nimi ze złości (od dobrych paru lat). Same wypadają: z kieszeni, z torebki, z samochodu. Kąpią się w wannie i w napojach. Psują się.
W połowie ubiegłego roku doszłam do wniosku, że nie stać mnie na kupowanie co dwa miesiące nowego dobrej klasy smartfona (w obliczu śmiertelnego wypadku poprzednika). Zdecydowałam się wtedy na zakup urządzenia niskiej klasy, za to w metalowej obudowie. Przesadziłam – nie działało na nim nawet quizzwanie. Na szczęście, problem szybko rozwiązał się sam. Pierwszego dnia telefon wypadł mi z ręki, kiedy przechodziłam przez przedpokój na piętrze. Pięknym lobem poszybował w stronę krętych schodów, po czym szybem w rogu spadł aż do samej piwnicy. Kiedy bez większych nadziei zeszłam na dół, okazało się, że urządzenie nadal działa, chociaż róg ma zauważalnie wgnieciony. Nabrałam do niego nowego szacunku. Dwa dni później wypadł mi z tylnej kieszenie spodni, kiedy szukałam kluczy na podjeździe – i zbił się na amen.
Wtedy wpadłam na radykalnie odmienny pomysł: kupić drogie i dobre urządzenie, o które będę drżeć i którego wobec finansowych rozterek z pewnością nie upuszczę na podłogę. Wybrałam najnowszy model Galaxy Note. Telefon był piękny, wygodny, szybki… Tyle że trafiłam na wadliwy egzemplarz, który restartował się w losowo wybranych momentach kilka razy dziennie. Przegapiłam (tygodniowy) termin na oddanie telefonu bez wyjaśnień w lokalnym Brand Store, ponieważ od razu pojechałam z nim na wakacje. Po powrocie najpierw wmawiano mi, że wyłączyłam telefon podczas aktualizacji i dlatego działa źle. Potem kilka razy zwracano mi go jako naprawiony – pierwszą rzeczą, jaką robił po włączeniu (najpierw w domu, potem już w sklepie), był restart. Pikanterii sprawie dodawał fakt, że byłam wtedy w bardzo zaawansowanej ciąży i brak sprawnego telefonu był coraz bardziej ryzykowny. Po około miesiącu użyłam na piśmie magicznego sformułowania “Miejski Rzecznik Konsumentów” i odzyskałam pieniądze. Nie chciałam już innego egzemplarza (o który dopominałam się od początku, ustaliwszy szybko, że problem jest sprzętowy i dotyczy konkretnej sztuki w ogólności dobrego modelu), bo nie miałam ochoty na jeszcze jedno spotkanie z serwisem.
Lato 2015, Mazury. Dawid i Gałgan ćwiczą pracę wodną. Ja łapię zasięg i reinstaluję Androida
Potem chciałam tanio i dobrze kupić One Plus. Powstrzymało mnie to, że właśnie pojawił się nowy model. Kosztował dokładnie tyle, co stary, ale do zakupu wymagane było posiadanie zaproszenia. Kto normalny kupiłby starą wersję, jeśli nowa kosztowała tyle samo?! A zaproszenie ciągle nie nadchodziło…
Wtedy wygasła umowa na usługi komórkowe w organizacji, do której należę i pojawiła się możliwość uzyskania bez dopłaty cuda w postaci Samsung Galaxy Xcover 3. Na kolana powala przede wszystkim rozdzielczość (480 x 800px), ale pancerny telefon nadal działa po dziesiątkach upadków, kąpieli i ugryzień. I takiego doświadczenia życzę wszystkim!
Pivotal to firma z, oczywiście, San Francisco. Jeden z jej produktów to chmura i oprogramowanie Pivotal Cloud Foundry . Pivotal Web Services to ich instancja dostępna publicznie. Umożliwia łatwe wdrażanie aplikacji napisanych w następujących językach i frameworkach: Java, Grails, Play, Spring, Node.js, Ruby on Rails, Sinatra, Go.
Moja aplikacja jest napisana w Spring Boot. A więc do dzieła!
Jak wdrożyć aplikację?
Wchodzimy na stronę http://run.pivotal.io/. Wita nas przyjazny komunikat o 87 dolarach w prezencie od firmy na cele testowania.
Bierzemy!
Rejestrujemy się w serwisie. Musimy podać numer telefonu, żeby przepisać otrzymany kod.
Firma uprzejmie prowadzi nas za rękę. Teraz pora na instalację PWS CLI, czyli konsoli do zarządzania aplikacją. Po instalacji logujemy się w sposób pokazany na stronie.
----->Downloading Spring Auto Reconfiguration1.10.0_RELEASEfrom https://download.run.pivotal.io/auto-reconfiguration/auto-reconfiguration-1.10.0_RELEASE.jar
(found incache)
...
0of1instances running,1starting
1of1instances running
App started
OK
...
state since cpu memory disk details
#0 running 2016-04-19 12:42:35 PM 0.0% 880K of 512M 66.3M of 1G
Podglądamy efekt na stronie.
Wdrożone!
Uczymy się korzystać z konsoli tekstowej oraz z widoku www.
Moja konkursowa aplikacja właściwie nic jeszcze nie robi – ot, wyświetla kilka widoków. Dzisiaj przetestowałam nowy drobiażdżek: wyświetlanie innej wersji strony na urządzeniach mobilnych.
Jak to zrobić? W Spring Boot to naprawdę prosta sprawa. Wystarczy:
Dopisać poniższą linię w pliku resources/application.properties (utworzyć plik, jeśli nie istnieje), nadpisując tym samym domyślnie ustawienie właściwości.
Zapisać mobilne wersje widoków w katalogu mobile (lub innym, jeśli zmienisz domyślną wartość właściwości spring.mobile.devicedelegatingviewresolver.mobilePrefix)
Mobilne wersje widoków
Po takim przygotowaniu możemy już podziwiać stronę w wersji normalnej i mobilnej. W swojej wersji mobilnej na teraz dodałam tylko słówko “MOBILE”. Wygląda to tak:
Wersja mobilna strony
Urządzenie mobilne udałam przy użyciu dość badziewiastego dodatku do Firefoksa o nazwie User Agent Switcher.
UPDATE: Marcin S. podpowiedział mi, że w Chrome można udawać urządzenie mobilne bez potrzeby instalowania żadnych dodatków:
Przyznaję, że ten wpis jest nieco wymuszony. Zasady Daj się poznać nakładają na mnie obowiązek wrzucania dwóch postów konkursowych tygodniowo. Tymczasem, choć informatycznie działo się u mnie sporo, projekt konkursowy w tym tygodniu leżał odłogiem. Dlaczego? Pomijając już przygody z nadgarstkiem – rozpoczęłam studia podyplomowe (przetwarzanie danych – big data)! Wczoraj, na moim dawnym wydziale, bawiłam się SQL-em, dzisiaj R. O R mam nawet anegdotę, ale na razie nie wymyśliłam pretekstu, żeby podciągnąć ją pod temat konkursowy.
Jeśli chodzi o moją aplikację Szafbook: nadal gnębi mnie kwestia dat. Pisałam już o tym, że HTML5 udostępnia pole wejściowe input typu date. Niektóre przeglądarki (Chrome!) wyświetlają przy nim elegancki widżet do wyboru daty z kalendarza. Inne nadal pozwalają/nakazują użytkownikowi samodzielne wpisanie wartości.
Nie byłam pewna, co z tym zrobić. Nie chcę rozbijać daty urodzenia na trzy osobne pola – za bardzo podoba mi się rozwiązanie z Chrome. Nie chcę wykrywać przeglądarki i podejmować decyzji w zależności od niej. Tak długo, jak będzie to możliwe, chcę się trzymać z dala od JavaScriptu.
Co tymczasowo wymyśliłam? Własny sposób tworzenia obiektu reprezentującego datę na podstawie danych wprowadzonych przez użytkownika, w którym pozwalam użytkownikowi na nieco większą elastyczność niż trzymanie się jednego sztywnego formatu daty.
Kod i uwagi do niego wklejam poniżej. Przy okazji zauważyłam kilka nowych (dla mnie) rzeczy w Javie. Najbardziej spodobały mi się grupy nazwane w wyrażeniach regularnych, wprowadzone w Javie 7. Dzięki ich istnieniu mogę wyciągnąć rok, miesiąc i dzień z tekstu dopasowanego do jednego z dwóch różnych wyrażeń regularnych. Bez nazw miałabym z tym większy problem, ponieważ numery grup (których trzeba było używać wcześniej) nie zgadzałyby się ze sobą (rok to grupa 1 w pierwszym wzorcu i grupa 3 w drugim).
W ogóle lubię wyrażenia regularne! 🙂 Na poprzednim blogu pojawił się kiedyś wpis o pułapkach regeksowych w Javie. Chyba przypomnę go w przyszłym tygodniu.
"Could not parse date: "+text+". Please use "+DATE_FORMAT.toPattern());
}
}
}
...
}
8. Użycie mojego własnego kodu do zamiany wartości tekstowej na typ Calendar.
17 i 18: Wyrażenia regularne mają umożliwić akceptację dat takich jak: 2016-04-10, 2016.04.10, 04/10/2014. Każde z wyrażeń definiuje 3 grupy nazwane: year, month oraz day.
39-41: Odwołuję się do grup, które są obecne niezależnie od tego, który z dwóch wzorców zadziałał.
43: Dość lekkie testy poprawności daty dopasowanej do wzorca. Zajrzałam do implementacji kalendarza. Podanie nieistniejącego dnia miesiąca nie powinno spowodować rzucenia wyjątku. Jeśli użytkownik będzie uparcie twierdził, że urodził się 30 lutego… Mało mnie interesuje, jaką datę ustawi mu system.
45: Ustawiam odpowiednią datę w moim kalendarzu. Początkowo chciałam mieć w tym miejscu obiekt typu Date, ale zorientowałam się, że tworzenie instancji Date w oparciu o rok, miesiąc i dzień od dawna nie jest zalecane.
Na koniec pozdrawiam z Warszawy! Przyjechałam z całą rodziną na konferencję 4Developers.
Okazało się, że standard HTML5 opisuje specjalne typy pól wejściowych dla adresu email oraz daty. W kodzie strony wygląda to tak:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<form action="#"method="post">
<table>
<tr>
<td>Email:*</td>
<td><input type="email"/></td>
</tr>
<tr>
<td>Birthday:</td>
<td><input type="date"/></td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
Email
Najpierw email. W Firefoksie mamy:
Walidacja adresu email w przeglądarce Firefox
W Chrome podobnie, ale dostajemy trochę więcej informacji:
Walidacja adresu email w przeglądarce Google Chrome
Data
Przy datach jest jeszcze ciekawiej.
Oto Chrome:
Pole do wprowadzania daty w Chrome, widok po załadowaniuPole do wprowadzania daty w Chrome, widok po najechaniu myszką i rozwinięciu menu
Co na to Firefox?
Firefox się nie wykazuje
Firefox nic, Explorer oczywiście także.
Boję się przeglądarki Chrome. Google czyta moje maile i wie, czego szukam w Internecie. To, co wpisuję w pasku adresowym jest dla mnie ostatnią granicą… Ale w tej chwili chciałabym, żeby wszyscy użytkownicy korzystali właśnie z niej 🙂
Mój zalążek projektu kompiluję i buduję przy użyciu Mavena. Maven (ang. “spec”) to narzędzie do budowania projektów napisanych w Javie. Sam Maven także został napisany w tym języku. Maven pozwala w uporządkowany sposób zarządzać zagadnieniami takimi jak: kompilacja, testowanie, budowanie, wynajdowanie i pobieranie zależności, generowanie dokumentacji.
Po co mi Maven, skoro mogę zbudować projekt w moim IDE? Po pierwsze, IDE nie zawsze jest pod ręką i może nagle okazać się, że na komputerze kolegi nie da się zbudować pilnie poprawionej wersji kodu, gdyż brakuje pięciu bibliotek (a po ich pobraniu – pięciu kolejnych, wymaganych przez te pierwsze). Po drugie, jeśli kod ma być testowany, a nawet wdrażany automatycznie, to przecież potrzebna jest możliwość uruchomienia go z zewnątrz. Po trzecie wreszcie – Maven to cała masa udogodnień i gotowych rozwiązań.
Konfigurację projektu mavenowego umieszcza się w pliku pom.xml. Plik ten można napisać samemu, ale najczęściej jest on generowany przez jakiś inicjalizator. W moim wypadku był to wspomniany już wcześniej Spring Initializr, ale najczęściej szablon generuje sam Maven w oparciu o żądany “archetyp”. Poniższe wywołanie stworzy szkielet aplikacji webowej z odpowiednio zainicjalizowanym plikiem pom.xml:
modelVersion: informacja o tym, z jakiego modelu DOM (Document Object Model) korzysta dany pom.xml. DOM w Mavenie zmienia się bardzo rzadko.
groupId: unikatowy identyfikator organizacji bądź grupy, która stworzyła projekt; najlepiej oparty o URL.
artifactId: nazwa głównego artefaktu generowanego przez projekt.
packaging: rodzaj wynikowego archiwum (e.g. JAR, WAR, EAR, etc.).
version: wersja powstałego artefaktu. SNAPSHOT oznacza wersję roboczą, do której się nie przywiązujemy.
name: wyświetlana (np. w dokumentacji) nazwa projektu.
description: krótki opis projektu
parent: (nieobowiązkowy) nadrzędny pom.xml, w którym zawarte są różne domyślne ustawienia projektu (np. kodowanie, konfiguracja pluginów).
properties: właściwości projektu. Ja podaję kodowanie źródeł oraz wersję Javy.
dependencies: zależności, czyli biblioteki potrzebne do zbudowania projektu. Jeśli nie ma ich na dysku, zostaną pobrane z repozytorium Mavena. Oprócz id grupy i artefaktu najczęściej trzeba podać także wersję, ale w tym wypadku wersjami podstawowych bibliotek zarządza wspomniany przed chwilą rodzic.
build: tu można zdefiniować podstawowe informacje na temat procesu budowania, np. lokalizację zasobów, profile budowania, niezbędne pluginy i ich konfiguracja. Pluginy w Mavenie mogą wszystko: potrafią przeprowadzić statyczną analizę kodu, przeformatować pliki itp. Moja aplikacja korzysta w tej chwili z pluginu Spring Boot Maven plugin, który umożliwia tworzenie uruchamialnych plików z aplikacją webową
Gdzie Maven przechowuje biblioteki?
Jeśli w systemie brakuje wymaganych bibliotek, Maven pobiera je ze zdalnego repozytorium http://mvnrepository.com lub innych wskazanych repozytoriówi umieszcza w repozytorium lokalnym.
Domyślna lokalizacja lokalnego repozytorium Mavena w systemie Windows
Oto najważniejsze standardowe polecenia Mavena (kolejność nie jest przypadkowa):
validate: sprawdź, czy projekt jest skonfigurowany w sposób umożliwiający jego zbudowanie.
compile: skompiluj kod, jeśli zmienił się od poprzedniego razu
test: przetestuj kod (testy jednostkowe, niewymagające wdrożenia).
package: w oparciu o skompilowany kod utwórz wynikowe archiwum (np. JAR czy WAR).
install: zainstaluj archiwum w lokalnym repozytorium Mavena. Projekt stanie się wówczas dostępny jako zależność dla innych projektów.
deploy: wdróż!
clean: usuń wszystkie artefakty wygenerowane przez poprzednie buildy.
site: wygeneruj dokumentację (HTML w oparciu o Javadoc).
Jak to wygląda w moim projekcie? W oparciu o pom.xml powstanie plik szafbook-0.0.1-SNAPSHOT.jar. Mogę go uruchomić w konsoli – zawiera wbudowany serwer Tomcata, na którym odpali się moja aplikacja.
1
2
mvn package
java-jar target/szafbook-0.0.1-SNAPSHOT.jar
Maven w konsoli
Mavena można podpiąć pod wszystkie najważniejsze IDE. W Eclipse będzie to wyglądało tak:
Maven zintegrowany z Eclipse
W kolejnym wpisie postaram się odpowiedzieć (sobie) na pytanie, czy w przypadku mojego projektu warto porzucić Mavena na rzecz coraz popularniejszego systemu budowania Gradle.
Bonus: troubleshooting. Gdzie mój pom.xml?! Eclipse domyślnie wyświetla pom.xml w postaci formularza. Poniższa ilustracja pokazuje, gdzie kliknąć, żeby dostać się do wersji tekstowej.
Przejście do wersji tekstowej w Eclipse
Milsza strona programowania
Ta strona korzysta z ciasteczek. Możesz zablokować je w opcjach przeglądarki, jeśli nie wyrażasz na nie zgody. Rozumiem