Archiwa kategorii: Tajemne szyfry (zaawansowane)

Bardzo techniczne wpisy dla programistów; niekoniecznie bardzo trudne, raczej – związane z konkretną technologią i mało uniwersalne

Maven ➝ Pivotal. Automatyczne wdrożenie w chmurze

Pisałam już o tym, jak uruchomić aplikację w chmurze Pivotal oraz jak podłączyć do niej bazę danych. W tym odcinku pokażę, jak ułatwić sobie życie i bez wysiłku wdrażać nowe wersje aplikacji zaraz po ich poprawnym zbudowaniu. Wykorzystam do tego plugin Cloud Foundry Maven (Cloud Foundry ≈ Pivotal).

Cel

Wdrożenie nowej wersji w chmurze po wydaniu jednego polecenia. Spoiler – w tym wypadku będzie to:

Realizacja celu

Plugin w pom.xml

Pierwszy tutorial, jak znalazłam w archiwach Cloud Foundry, twierdził, że pluginu nie ma w głównym repozytorium Mavena – konieczne miało być dodanie wpisu w sekcji pluginRepositories pliku pom.xml. Od tego czasu sytuacja uległa zmianie (https://mvnrepository.com/artifact/org.cloudfoundry/cf-maven-plugin) i nie trzeba już zawracać sobie tym głowy.

Konfiguracja pluginu w pom.xml

W moim wypadku wygląda tak:

Czym jest mycloudfoundry-instance? O tym poniżej.

Dane logowania

Dane logowania można podać w konsoli (ale wtedy nie będzie to wdrożenie za pomocą jednego polecenia!), w treści pom.xml (ale wtedy trafią one do systemu kontroli wersji, być może publicznego!), albo zdefiniować je w przeznaczonym dla Mavena pliku settings.xml na własnym dysku. Więcej o samym pliku settings.xml można poczytać tutaj. Szukać go należy (lub utworzyć) na jednej z dwóch ścieżek:

  • ${maven.home}/conf/settings.xml (dla całej instalacji Mavena)
  • ${user.home}/.m2/settings.xml (per użytkownik)

U mnie wpis wygląda tak:

id to nadana przeze mnie nazwa, za pomocą której mogę odwołać się do tej konfiguracji w pliku pom.xml (linia 6 w listingu z konfiguracją).

Więcej informacji

Więcej informacji na temat korzystania z pluginu można znaleźć na stronie https://docs.run.pivotal.io/buildpacks/java/build-tool-int.html.

Napotkane problemy

Wcześniej musiałam tylko raz wyklikać powiązanie pomiędzy aplikacją a usługą MongoDB (mLab). Przy wdrożeniach przez konsolę cf konfiguracja ta była zachowywana. Dlatego początkowo pominęłam definicję serwisów w konfiguracji pluginu (linie 13-21). Błąd! – baza danych była przez to „odczepiana” od aplikacji przy wdrożeniu.

Kod

Jak zwykle w GitHub.

PS.

Wiedziałam, że po zakończeniu konkursu Daj się poznać wrócę do swojego projektu, ale nie byłam pewna, czy chcę na ten temat blogować. Co mnie przekonało? To, że kiedy zapomniałam, jakie czynności należy wykonać w celu podłączenia bazy danych do aplikacji w chmurze, nie musiałam szukać daleko. Zerknęłam jedynie we własne wcześniejsze teksty! W ten sposób zostałam swoim najwdzięczniejszym czytelnikiem.

Strumienie w Javie 8

Odkrywam na poważnie Javę 8. Jestem tak zachwycona strumieniami, że aż się muszę podzielić!

Przykład (Java 8)

Zacznę od przykładu. Napisałam ostatnio taki kod:

Co robi ten fragment? Zwraca tablicę zawierającą te klucze z mapy, których wartości są większe od 0.

Dokładniej:

  1. Odwołuje się do obiektu mapy (map).
  2. Pobiera zbiór par klucz-wartość przechowywany w tej mapie (entrySet()).
  3. Zamienia ten zbiór na strumień, albo, poprawniej, odczytuje ten zbiór poprzez strumień (stream()).
  4. Filtruje strumień, pozostawiając w nim (a właściwie w nowym strumieniu) tylko te pary klucz-wartość, w których wartość jest większa od zera. Do filtrowania używana jest lambda, czyli definiowana w miejscu anonimowa funkcja zwracająca wartość typu boolean, przekazana jako parametr (.filter(entry -> entry.getValue() > 0)).
  5. Na wszystkich elementach (typu Map.Entry) pozostałych w strumieniu wykonywana jest funkcja zwracająca klucz (.map(entry -> entry.getKey())).
  6. Na samym końcu wywoływana jest metoda toArray z interfejsu Stream, do której przekazujemy generator (toArray(String[]::new)), czyli funkcję, dzięki której strumień wie, ile pamięci zaalokować. Istnieje bezargumentowa wersja tej metody, tyle że przy jej użyciu otrzymamy tablicę obiektów typu Object.

Przykład (Java 7 i starsze)

Po napisaniu tych paru linijek (ciągle mam problemy z formatowaniem takiego kodu i liczeniem linii) zaczęłam się zastanawiać, jak wyglądałby ten kod w wersji przedstrumieniowej. Musiałoby to być coś takiego:

Porównanie

Kilka różnic:

Java 7 Java 8
Definicja zadania Algorytm / opis procedury Strumień przetwarzania / deklaracja oczekiwanego wyniku
Kolejność przetwarzania Sekwencyjna Nieokreślona – może zostać poddana optymalizacji
Typy danych W niektórych miejscach, jak pętla for, trzeba je powtórzyć Kompilator jest w stanie je wywnioskować, nie trzeba ich powtarzać (np. w definicjach lambd)
Układ kodu Kod z blokami i wcięciami Łańcuch wywołań (oczywiście w przypadku bardziej złożonych lambd również trzeba stosować bloki)

Uczciwie dodam, że po stronie minusów stosowania strumieni muszę na razie dopisać debugowanie. W przypadku wystąpienia problemu, odrobinę trudniej jest mi się zorientować, co dokładnie poszło nie tak.

Czy naprawdę nie było innego wolnego słowa?

Strumieni z Javy 8 (z pakietu java.util.stream) nie należy mylić ze strumieniami służącymi do obsługi wejścia i wyjścia (z pakietu java.io). Co za geniusz wymyślił tę nazwę – nie wiem. Być może chodziło i uniknięcie jeszcze gorszego słowa „monada”.

Jeśli chcesz wyszukać w Google informacji na temat „nowych” strumieni, używaj nazwy „Java 8 Streams”.

Co się dzieje w tle?

Strumień nie przechowuje własnych danych – można uznać go za „widok” na dane pochodzące z kolekcji lub innego źródła. Podczas potokowego przetwarzania danych na przykład za pomocą kilku kolejnych operacji map, strumień nie jest modyfikowany, tylko zwracany jest wtedy nowy strumień, oferujący inne dane.

Wniosek

Wygodne. Używać.

Kiedy po raz pierwszy, w ramach prezentacji na Poznań JUG, zobaczyłam strumienie, nie byłam zachwycona. Pomyślałam sobie, że to kolejny wynalazek, który świetnie działa na wymyślonych, zabawkowych przykładach, a który okazuje się bezużyteczny w prawdziwym świecie. Nic bardziej mylnego, w pracy korzystam z nich teraz regularnie.

PS. Sklejanie Stringów

Od lat jeden z najbardziej irytujących braków w bibliotece standardowej Javy to sklejacz łańcuchów znaków. Teraz, jeśli chcesz za pomocą np. średników połączyć słowa przechowywane w kolekcji, nie już musisz pisać specjalnej funkcji ani załączać zewnętrznych bibliotek. Można tak:

Baza danych w chmurze Pivotal (photo story)

Pokazywałam już, jak wdrożyć aplikację webową w chmurze PaaS Pivotal Web Services:

  1. Wdrażam moją aplikację (która nadal nic nie robi) w chmurze Pivotal Web Services

Pisałam też co nieco o nierelacyjnej bazie danych MongoDB:

  1. Jak zacząć z MongoDB?
  2. MongoDB + Spring Data
  3. Zapisuję dane użytkowników w bazie i robię głupie błędy podczas walidacji formularza

Jak zapewnić sobie dostęp do serwera bazodanowego działającego w chmurze?

Do tej pory uruchamiałam aplikację tylko lokalnie. Przed odpaleniem pliku .jar z wbudowanym serwerem Tomcat musiałam najpierw uruchomić lokalny serwer bazy danych.

W serwisie Pivotal istnieje tzw. Marketplace, w którym można kupować dodatkowe usługi. Wygląda to tak:

Lista dodatkowych serwisów Pivotal
Lista dodatkowych serwisów Pivotal

Odnalazłam na liście interesujący mnie element:

MongoDB w chmurze Pivotal
MongoDB w chmurze Pivotal

Moim celem jest teraz włączenie serwisu i powiązanie go z moją aplikacją Szafbook.

Mogę zrobić to z wiersza poleceń przy użyciu narzędzia cf (muszę wówczas użyć poleceń cf create-service, cf bind-service i ewentualnie cf-restage), mogę też wyklikać swoje wymagania w przeglądarce, na stronie Pivotal:

Banalnie prosta konfiguracja usługi
Banalnie prosta konfiguracja usługi

W przypadku wielu dodatkowych serwisów muszę wybrać pomiędzy kilkoma „planami” – najczęściej jeden z nich jest darmowy i dość ograniczony (np. 5 jednoczesnych połączeń, 20MB pojemności), a kolejne są coraz mocniejsze i coraz droższe. MLab na razie ma tylko darmową wersję.

Po kilkunastu sekundach mam już działające połączenie pomiędzy bazą a aplikacją:

Powiązanie serwisu MLab (MongdoDB as a service) z aplikacją w chmurze Pivotal Web Services
Powiązanie serwisu MLab (MongdoDB as a service) z aplikacją w chmurze Pivotal Web Services

Wszystko działa!

Działająca aplikacja :)
Działająca aplikacja 🙂

Chmura jest w stanie samodzielnie podmienić źródło danych aplikacji w taki sposób, by odwoływało się do nowoutworzonego serwisu. Programista nie musi nawet znać adresu tej usługi! Choć, oczywiście, może go sobie podejrzeć:

Więcej informacji na temat instancji MongoDB podpiętej do mojej aplikacji (na stronie dostawcy usługi)
Więcej informacji na temat instancji MongoDB podpiętej do mojej aplikacji (na stronie dostawcy usługi)

W rzeczywistości, jak zwykle, nie obyło się bez błędów:

  1. Opierając się o tutorial, wyklikałam dostęp do serwisu ElephantSQL, czyli bazy relacyjnej bazy PostgreSQL zamiast do MLab czyli MongoDB.
  2. Rozdzieliłam właściwości projektu pomiędzy kilka plików dla różnych profili (np. application-development.properties), po czym zapomniałam zmienić profil (z development na cloud) przy budowaniu na potrzeby wdrożenia.

 

 

Zapisuję dane użytkowników w bazie i robię głupie błędy podczas walidacji formularza

Zapis danych użytkowników w MongoDB

O MongoDB pisałam już w dwóch wcześniejszych postach: Jak zacząć z MongoDB? i MongoDB + Spring Data.

Dzisiaj postanowiłam zaimplementować wreszcie zapis danych z formularza użytkownika w mojej nierelacyjnej (dokumentowej) bazie danych.

W poprzednim tekście pokazałam, jakich adnotacji należy użyć w klasie User, żeby jej instancje zostały poprawnie zapisane w bazie danych. Żeby faktycznie zapisać dane z formularza, musiałam tylko dopisać kilka linii w kodzie kontrolera:

Co zrobić z hasłami?

Zależało mi na spełnieniu trzech warunków:

  1. Użytkownik podaje hasło dwa razy, żeby zmniejszyć ryzyko literówki.
  2. Wpisywane hasło jest niewidoczne.
  3. Hasło w bazie danych nie jest przechowywane w postaci jawnej.

Dwa hasła

Dodałam następujące pola do klasy reprezentującej użytkownika:

Specjalnie zaznaczyłam adnotację @Transient. Dzięki jej użyciu wskazane pola (w tym wypadku hasła zapisane jawnym tekstem) nie zostaną zapisane w bazie danych.

Ukrycie literek

Pola musiałam również dopisać do formularza. Użyłam pola input w wersji password, żeby zamiast liter wyświetlić gwiazdki (czy tam kropki).

Funkcja hashująca

Wreszcie, hasła. Nie powinny być przechowywane w bazie danych w postaci jawnej. Powodów jest wiele. Na przykład taki: ludzie często używają tych samych haseł w wielu miejscach. Gdyby administrator bazy danych miał wgląd w hasła do aplikacji, mógłby podjąć próbę zalogowania się na konta użytkowników w innych serwisach (np. gmail) przy użyciu tego samego hasła i adresu email.

Oprócz pokazanych już dwóch pól do wprowadzenia hasła roboczego, klasa User przechowuje jeszcze następujące dwie wartości:

passwordSalt to losowa wartość doklejana do hasła przed jego „zaszyfrowaniem” (dzięki niej w bazie danych nie będzie widać, że dwa użytkownicy przypadkowo użyli tego samego hasła), passwordEncrypted to wynik szyfrowania.

Losowanie i szyfrowanie realizuje następujący pomocniczy serwis, wywoływany z kontrolera dzięki adnotacji @Autowired:

Kontroler wygląda tak:

Błąd 1: strona błędu zamiast komunikatu o błędzie w formularzu

Podanie niepoprawnych danych w formularzu z nieznanego mi powodu zaczęło przenosić mnie na moją własną stronę błędu (zamiast wyświetlenia komunikatu przy polu formularza, które spowodowało problem).

Co się okazało?

W pewnym momencie zmieniłam sygnaturę metody kontrolera z:

na:

ponieważ chciałam dopisać informacje do modelu.

Okazuje się, że parametr reprezentujący BindingResult musi następować bezpośrednio po parametrze, przez który przekazywany jest dotyczący go obiekt (co ma sens, gdy jeden formularz obsługuje wiele obiektów).

Formularz zaczął działać poprawnie po zamianie kolejności parametrów:

Błąd 2: Error during execution of processor ‚org.thymeleaf.spring4.processor.attr.SpringErrorsAttrProcessor’

Kolejny błąd zaskoczył mnie, kiedy testowałam sytuację, w której użytkownik wpisze dwa różne hasła. Okazało się, że poczyniłam złe założenia na temat parametrów metody rejectValue, za pomocą której chciałam przekazać do formularza informację o wykryciu problemu. Napisałam:

zakładając, że drugi parametr to komunikat o błędzie. Tymczasem – to kod błędu. Komunikat można przekazać dopiero w trzecim parametrze (poprawny kod widać na listingu z metodą processPasswords). Błąd wynikał zatem z braku komunikatu o błędzie, który był wymagany w szablonie.

Wreszcie działa

Kod jest dostępny w moim repozytorium GitHub.

Komunikat o niepasujących do siebie hasłach
Komunikat o niepasujących do siebie hasłach
Wyświetlenie informacji o pierwszym lepszym użytkowniku z bazy
Wyświetlenie informacji o pierwszym lepszym użytkowniku z bazy

 

Jak zabezpieczyć strony generowane przez Spring Boot Actuator?

W mojej aplikacji webowej korzystam z biblioteki Actuator, która daje dostęp do masy pożytecznych informacji na temat działania aplikacji (w czasie rzeczywistym).

Wystarczy dodać w pom.xml następujący wpis:

Po uruchomieniu aplikacji uzyskuję dostęp do wielu stron ze szczegółami działania aplikacji. Przykładowo, pod adresem /health znajdę podstawowe informacje o aktualnym statusie aplikacji:

Raport na temat stanu zdrowia aplikacji
Raport na temat stanu zdrowia 🙂

Wszystko pięknie, tyle tylko, że nie chcę dzielić się takimi danymi z przypadkowymi (lub, co gorsza, nieprzypadkowymi) użytkownikami Internetu.

Pokazywałam już (we wpisie Spring Boot: Bezpieczeństwo 101) jak oznaczyć strony, które mają być wyświetlane tylko użytkownikom o statusie administratora. Problem z Actuatorem polega na tym, że generowane przez tę bibliotekę adresy nie mają wspólnego rdzenia. Mamy np.: /env, /metrics, /trace. Ich wymienianie jest nieco upierdliwe!

Wtem! Wyczytałam (w Spring Boot in Action), że można dodać tym stronom wspólny przedrostek za pomocą wpisu w pliku application.properties:

A następnie zabezpieczyć dostęp do stron na tej ścieżce:

I voilà! Po zalogowaniu się jako administrator widzę:

Raport o stanie zdrowia wyświetlam teraz pod innym, wspólnym adresem
Raport o stanie zdrowia wyświetlam teraz pod innym, wspólnym adresem

 

MongoDB + Spring Data

W poprzednim wpisie pokazałam, jak dodawać dokumenty i kolekcje do nierelacyjnej bazy MongoDB oraz jak połączyć się z tą bazą z kodu javowego.

W tym odcinku pokażę, jak łatwo utrwalić w MongoDB obiekty przetwarzane w aplikacji webowej.

Wpis w pom.xml

Kolejny raz korzystam ze „startera” Spring Boot, który grupuje w sobie kilka podstawowych bibliotek realizujących spójny cel – w tym wypadku mapowanie, zapis i odczyt danych z MongoDB:

Adnotacje w obiekcie, który ma zostać utrwalony w bazie danych

Do obiektu reprezentującego użytkownika bazy danych dodałam następujące adnotacje:

  • @Document oznacza, że mamy do czynienia z obiektem zapisywanym w bazie danych. Jest to odpowiednik @Entity w wersji Spring Data dla relacyjnych baz danych.
  • @Id to automatycznie generowany, obowiązkowy identyfikator dokumentu w ramach kolekcji w MongoDB.
  • Pola nieopisane zostaną bezpośrednio odwzorowane w dokumencie w bazie danych.
  • @DBRef oznacza odwołanie do innej kolekcji w bazie. Uwaga! Nie wolno tworzyć odwołań obustronnych, MongoDB brzydko się przez nie zapętli. Zamiast tego należy po jednej stronie jawnie zapisać identyfikator dokumentu.

Interfejs do obsługi repozytorium

Muszę tylko zasygnalizować, że będę chciała pobierać z bazy obiekty danego typu:

Użycie w kodzie: kontroler i widok

Kontroler

W celu przetestowania konfiguracji dodałam odwołanie do kolekcji użytkowników do mojego powitalnego kontrolera GreetingController:

Nazwa pierwszego napotkanego użytkownika zostanie dodana do modelu.

Widok

W powitalnym szablonie Thymeleaf dopisałam kod, który wyświetli nazwę odnalezionego w bazie danych użytkownika, o ile taki użytkownik istnieje:

Efekt

Wyświetlona informacja pobrana z bazy danych
Wyświetlenie informacji pobranej z bazy danych. Przecinek od czapy.

Zapis informacji w bazie

Mogę skorzystać ze sposobu pokazanego w poprzednim wpisie. Mogę też przygotować kod, który będzie wywoływany przy każdym uruchomieniu aplikacji:

Konfiguracja połączenia z bazą danych

Tym razem oparłam się na wartościach domyślnych. Nie wpisałam w application.properties żadnych danych na temat połączenia z bazą danych. Oczywiście musiałam uruchomić mój serwer MongoDB.

Wszystko zadziałało.

Mogę podejrzeć zastosowane wartości właściwości związanych z MongoDB dzięki dołączonej w pom.xml bibliotece Spring Boot Actuator. Pod adresem http://localhost:8080/configprops widzę m.in. takie dane:

Test to baza danych, którą utworzyłam na potrzeby poprzedniego wpisu.

Troubleshooting: co zrobiłam nie tak przy pierwszym podejściu?

  • Zapomniałam o wpisaniu @Autowired w kontrolerze w punkcie 4 (komunikat CrudRepository ... is no accessor method!)
  • Nie włączyłam MongoDB (Timed out after 10000 ms while waiting for a server that matches AnyServerSelector{} ... caused by {java.net.ConnectException: Connection refused: connect}})

Własny formularz logowania Spring Security

W poprzednim wpisie pokazałam, jak zabezpieczyć stronę lub widok hasłem. Korzystałam tam z domyślnego formularza logowania, który wyglądał tak:

Standardowy formularz logowania
Standardowy formularz logowania

Zawsze fajnie dostać coś za darmo, ale nie pasuje to wyglądem do reszty strony.

Co zrobiłam, żeby wymienić formularz na ładniejszy?

  1. Dopisałam odpowiednie fragmenty w metodzie configure wprowadzonej w poprzednim wpisie klasy SecurityConfiguration:
  2. Zdefiniowałam formularz w pliku login.html:
  3. Dodałam najprostszy możliwy kontroler:

Efekt wygląda tak:

Ulepszony formularz logowania
Ulepszony formularz logowania

Spring Boot: Bezpieczeństwo 101

Pora zabezpieczyć stronę – niektóre jej części chcę pokazywać tylko zalogowanym użytkownikom.

Ukrycie całej strony za hasłem

Ukrycie całej strony za hasłem jest wyjątkowo proste. Wystarczy dodać do pom.xml następującą zależność:

Strona wygląda wtedy tak:

Cała strona ukryta za hasłem
Cała strona ukryta za hasłem

Skąd wiem, jakiego podać użytkownika i hasło?

  • Jeśli nic dodatkowo nie konfigurowałam, mogę zalogować się przy użyciu nazwy użytkownika User oraz hasła zapisanego w logach serwera:
  • Mogę też podać dane logowania w pliku application.properties:

Zabezpieczenie na poziomie stron i metod

Dodaję do swojego kodu następującą klasę konfiguracyjną:

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.

Jak to wygląda na stronie?

Strona główna, niewymagająca logowania
Strona główna, niewymagająca logowania
Standardowy formularz logowania
Standardowy formularz logowania
Użytkownik zalogowany
Użytkownik zalogowany

Dodatkowo mogę definiować wymagania bezpieczeństwa w kodzie, przy każdej metodzie kontrolera, np.:

Kod jest dostępny w GitHubie.

Co dalej?

Mobilna wersja strony w Spring Boot

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:

  1. Dodać jedną zależność w pliku pom.xml
  2. 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.
  3. 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
    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:

Tryb mobilny w Google Chrome

UPDATE2: Historie komórkowe

Odkrywam nową Javę przy okazji dat

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.

  • 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.