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:

Programiści i ich żony

Dawno nie było tu tyrad równościowych. Spieszę naprawić ten karygodny błąd! Uprzedzam od razu, że będzie dość osobiście.

W mrokach dziejów (na starym blogu)

Na moim poprzednim blogu sporo było uniesień. Dokładniej, to ja sporo się unosiłam 🙂 Było mi to wtedy potrzebne – zaczynając pracę w zdominowanym przez facetów zawodzie czułam się dość osamotniona. Dużo rzeczy mnie zaskakiwało i nie zawsze potrafiłam w porę reagować. Zdarzyło mi się, przyznaję, ronić łzy w firmowej toalecie. Pierwszy raz po (nie)sławnej przygodzie z quasiprogramistą… Ale to materiał na zupełnie inną historię. W każdym razie, w tamtych czasach blog pełnił dla mnie (między innymi) rolę wentyla bezpieczeństwa.

Dzisiaj (ku słońcu)

Wiele się od tego zmieniło. Po pierwsze, odkryłam, i bardzo mnie to uspokoiło, że nie jestem ani osamotniona, ani wyjątkowa. Programujących kobiet są miliony. Wiele z nich poznałam i wiele mnie inspiruje: Jessica z Abstractivate, dziewczyny z Daj się poznać (trzy spośród siedmiorga zwycięzców to kobiety), moja uzależniona od Coursery przyjaciółka Ewa, Trisha Gee., Ta ostatnia zaskoczyłą mnie kiedyś (na GeeConie) tym, że nie chciała rozmawiać o kobietach w IT. Dopiero niedawno  zrozumiałam, dlaczego, a przynajmniej tak mi się wydaje: bo kobiety nie są ułomkami i nie potrzebują specjalnego traktowania.

Po drugie, trochę wbrew kulturze i wychowaniu, nauczyłam się wreszcie asertywności. W obliczu seksistowskich uwag i nierównego traktowania, reaguję. Uprzejmie i konstruktywnie, ale ze świadomością, że upomniana osoba prawdopodobnie i tak zachowa urazę.  Zazdroszczę osobom, które w takich sytuacjach potrafią rozładować sytuację humorem. To ważny sprzymierzeniec, ale ja po przejściu w tryb obrońcy kobiecej robię się śmiertelnie poważna.

Po trzecie, po paru latach pracy w docenianym i opartym na umiejętnościach zawodzie człowiek nabiera wreszcie pewności siebie.

W związku z tym, w nowym miejscu narzekania prawie na uświadczycie. Nie znaczy to jednak, że wszystko jest w idealnym porządku i że nic nie wyprowadza mnie z równowagi. Kobiet w zawodzie nadal jest za mało (nie twierdzę, że ma być połowa, ale 5%  pewnością nie odwzorowuje rzeczywistego rozkładu predyspozycji) i nie jest to przypadek. Najbardziej martwią mnie   drobne korekty kursu, którym poddawane są kobiety (a jeszcze częściej dziewczynki) utalentowane w dyscyplinach ścisłych. „Chłopcy z reguły lepiej radzą sobie z geometrią”, żart o śwince morskiej (nie cytuję, bo jest durny, a i tak wszyscy go znają), sekcja z zabawkami „dla dziewczynek”… I potencjalna programistka zostaje nauczycielką w klasach 1-3. Z całym szacunkiem dla tych drugich.

Do rzeczy (a stereotypy się obalą, obalą)

Stereotypy to utarte wzorce, którymi posługuje się nasz mózg, by nie przekroczyć dostępnej mocy obliczeniowej. Są potrzebne. Niejednokrotnie ratowały naszym przodkom tyłki oraz pozostałe części ciała. Wobec wystającego zza krzaka pasiastego ogona, nagradzaną przez ewolucję strategią była natychmiastowa ucieczka. Czcigodny przodek nie miał czasu na gdybanie, czy może w danym wypadku nie am do czynienia z krwiożerczym tygrysem, tylko większą wiewiórką, która upaprała w błocie tylną część ciała. Pasiasty ogon = tygrys = ucieczka.

Stereotypy są niezbędne, dlatego nie widzę nic strasznego w tym, że na hasło „informatyk” przed oczami wielu pojawiają się panowie z IT Crowd. To miłe i ważne, kiedy Lego wypuszcza zestawy z kobietami-naukowcami, ale nie będę strofować swojego dziecka, jeśli narysuje szalonego naukowca lub górnika płci zgodnej z rodzajem gramatycznym.

Jest jednak granica i w tym wypadku widzę ją tam, gdzie kolejna napotkana osoba w rozmowie ze mną bez cienia refleksji i poczucia obciachu zakłada, że programista musi mieć żonę.

Jak jest?

W środowisku biznesowym powinno być oczywiste (dla wszystkich: działów HR, menedżerów, organizatorów szkoleń), że stereotyp to jedno, a życie (i korzyść finansowa) to zupełnie inna sprawa. Wygląda na to, że oczywiste to jednak ne jest. Z tej okazji dzisiaj chcę podzielić się  wspomnieniami kilku sytuacji, które ostatnio przyprawiły mnie o ból głowy.

W pracy
W pracy

Studium przypadku 1: oferty pracy

Dostaję czase oferty pracy, która wyglądają mniej więcej tak:

„Pani Justyno!

Chcielibyśmy zaproponować Panu…
Jeśli jest Pan zainteresowany…”

Koleżanko, skusiłabyś się na taką ofertę?

Przynajmniej jeden z tych tekstów został wysłany przez rekrutera płci męskiej – pana od HR. Ironia odbiła się od ściany jak groch.

Studium przypadku 2: podatki

Raz na jakiś czas przekraczam próg podatkowy. Ostatnim razem odebrałam maila od działu finansowego, w którym informowano mnie życzliwie, że mogę uniknąć podwyższenia podatku, jeśli rozliczę się z żoną.

No tak. Skoro zarabiam stosunkowo dużo, to pewnie jestem facetem i imię nie ma tu nic do rzeczy. Moja żona pewnie zarabia mniej (hello!), więc razem tego progu nie przekroczymy. Podpisane przez miłą panią z działu finansowego.  Ile wysiłku potrzeba, żeby w szablonie maila wyraz „żona” zamienić na neutralnego „małżonka”?

Studium przypadku 3: blogowanie

Należę do pewnej grupy wsparcia prelegentów i blogerów IT. Ostatnio pojawił się w niej taki oto post:

Jak udaje wam się godzić pasję z obowiązkami wobec żony i rodziny?

Problemów, a może warstw problemów, widzę tu  kilka. Czy w przypadku IT ja sama w ogóle mogę mówić o pasji? Czy ktoś, kto traktuje żonę jak obowiązek, powinien wchodzić w związek małżeński? Ale przede wszystkim: O co, u licha, chodzi z tą żoną? Dlaczego w domyśle żona informatyka ona nie ma pasji? Dlaczego ja, jako bloger i okazjonalny prelegent, jestem wykluczana z dyskusji z racji nieposiadania żony? Dodam jeszcze, że lista członków grupy jest jawna. Łatwo sprawdzić, ile wśród nich kobiet.

Pod rozwagę.

Dlaczego tak, jak jest, jest źle?

Założenie, że doświadczony programista to mężczyzna (najlepiej heteroseksualny i pozostający w uświęconym sformalizowanym monogamicznym związku) jest tyleż szkodliwe, co nieprawdziwe.

Wspólny mianownik jest tu następujący – wszyscy na tym tracą. Traci branża, w której ciągle brakuje pracowników. Tracą kobiety, spychane do gorzej płatnych zawodów. Tracą mężczyźni, skazywani na pracę w kompletnie sztucznym środowisku.

A może byśmy z tym skończyli?

Co zrobić, żeby było lepiej?

Weryfikować poglądy. Sprawdzać płeć odbiorcy maila.

Wierzyć w siebie.

Mam trochę mieszane uczucia odnośnie akcji zachęcających dziewczyny do kodowania. Wiem, że niektórzy odbierają je jako coś niesprawiedliwego, pytając, dlaczego nie ma takich pomocy dla facetów. W przedsięwzięciach tych nie chodzi jednak o parytety ani zajęcia wyrównawcze, tylko o przekonanie pań, że informatyka jest zawodem także dla nich. Zwłaszcza tych pań, którym wcześniej na różnych etapach edukacji uparcie wmawiano, że tak nie jest.

Oto garść pomocnych linków:

Pełne rozterek niewiasty zapraszam również do kontaktu ze mną.

Oświadczenie A (o pierwszym świecie)

Owszem, poruszam tu problem pierwszego świata. Jednak to, że gdzieś na świecie ludzie mają nieporównywalnie gorzej od nas, nie powinno nikogo powstrzymywać przed dążeniem do perfekcji.

Oświadczenie B (o dobrych chęciach)

Często, kiedy opowiadam którąś z moich wykluczeniowych przygód,  spotykam się z zapewnieniami, że „oskarżana” przeze mnie osoba nie miała złych intencji. Wiem. Wierzę. Ale to nic, naprawdę nic nie zmienia.

Oświadczenie C (o kobietach, którym się to nie przytrafia)

Są kobiety, które twierdzą, że problemu  nie ma. Spotkałam takich kilka – to zdecydowana mniejszość spośród znanych mi programistek. Jak to możliwe? Moim zdaniem albo mają bardzo grubą skórę (zazdroszczę!), albo wyjątkowo przyjazne środowisko pracy, albo (o zgrozo) zgadzają się na status quo i hipotezę, że po prostu mało kobiet nadaje się do naszej pracy.

PS. (o serialu)

A w ogóle, skoro mowa o kobietach-naukowcach, to uwielbiam serial Orphan Black!!! Dostępny na polskim Netfliksie.

PS2. (o „Daj się poznać”)

Być może nie wynika to jednoznacznie z tego wpisu, ale zajęłam 3. (na prawie 300!) miejsce z konkursie Daj się poznać. Jestem z tego niesamowicie dumna.

Finalistka milczy (pseudowpis statusowy)

Wraz z majem zakończył się konkurs Daj się poznać, w którym z maniakalnym uporem brałam udział od samego początku do samego końca.

Wygląda na to, że dostałam się do ścisłego finału! Lista finalistów jest dostępna tutaj, razem z linkiem do ankiety, w której można oddać na mnie głos. Byłoby mi bardzo miło, gdybym wygrała wypasione krzesło. Głosującym dam w nim posiedzieć i jeszcze podam piwo 😉

Listę moich wpisów konkursowych można znaleźć w poprzednim tekście: Podsumowanie mojego udziału w konkursie „Daj się poznać”.

Przy okazji, krótkie wyjaśnienie, dlaczego nie opublikowałam nic przez ostatni tydzień:

  1. Potrzebowałam chwili odpoczynku. Zaraz wracam. Także z projektem konkursowym, chociaż pierwszy będzie luźniejszy tekst. Roboczy tytuł to „Żony programistów”. Brzmi intrygująco? 🙂
  2. W normalnych okolicznościach jestem zadowolona z dwóch wpisów w miesiącu. Czy obserwowany ostatnio zalew treści nie był męczący także dla Czytelników? Chętnie się dowiem.
  3. Poważnie rozważam zmianę pracy (moje biuro przeniosło się do innego miasta) i biorę udział w 6!!! procesach rekrutacyjnych. Nie mogę się doczekać, żeby podzielić się uniwersalną częścią tych doświadczeń na blogu 🙂
  4. Nadal jestem na urlopie macierzyńskim, ale przyjęłam zleconko. Któremu się napuchło.
  5. Padł mi komputer. Serio. Skoro już i tak uprawiam w tym wpisie prywatę, rozwinę myśl. Wymieniłam właśnie przedpotopową stacjonarkę na laptopa (ThinkPad). W związku z tym przestał mi być potrzebny netbook. Chcę go sprzedać. Jest to lekki, wygodny i szybki Samsung z serii 900X (konkretnie NP900X3C). Oryginalnie z Windows 8, teraz działa na nim (jak marzenie) Linux Mint. Chętnych zapraszam do kontaktu.

Bardzo dziękuję za dotychczasowe wsparcie.

Do zobaczenia wkrótce!

Podsumowanie mojego udziału w konkursie „Daj się poznać”

O co chodzi?

Od początku marca biorę udział w konkursie Daj się poznać, organizowanym przez Macieja z bloga Devstyle. Konkurs oficjalnie kończy się jutro.

Na stronie agregującej blogi zgłoszone do konkursu wyraźnie widać trend podsumowań. Niniejszym do niego dołączam. Pozwolę sobie również przypomnieć tekst, który napisałam na półmetku: Konkurs „Daj się poznać”: status i wrażenia.

#trending
#trending

Jakie były założenia?

Proste! Przez co najmniej 10 tygodni na przestrzeni 3 miesięcy (marzec-maj) należało rozwijać swój projekt open source i blogować na jego temat.

Miara mojego wysiłku w konkursie "Daj się poznać"
Miara mojego wysiłku w konkursie „Daj się poznać”

Wymagania wobec mojego projektu opisałam w pierwszym konkursowym wpisie: Dam się poznać: mój projekt open source.

Z czego jestem zadowolona?

  • Projekt istnieje –  w GitHubie, na blogu, na własnej stronie.
  • Podczas pracy zdążyłam się pobawić mniej więcej połową technologii z mojej listy: Java 8, Spring Boot, Maven, Git/GitHub, MongoDB, chmura Pivotal.
  • Mój blog odwiedziło sporo nowych osób.
  • Konkurs wygenerował materiał w blogosferze, z którego będziemy wspólnie korzystać przez następne miesiące.
  • Nabrałam zapału do pracy – nie mogę się doczekać, aż powiem koleżankom, że mogą już katalogować swoje ciuchy na mojej stronie 🙂
  • Konkurs spowodował znaczny przyrost treści na moim blogu: w tej chwili połowa wpisów to wpisy konkursowe. Sądzę, że większość z nich jest przyzwoitej jakości i może się komuś przydać.
  • Wbrew oczekiwaniom, w czasie trwania konkursu udało mi się napisać kilka tekstów na inny temat, na przykład ten: Ku przestrodze: przygody nadgarstkowe. Aha, no i miałam operację!
  • Osoby, które lubią tu zaglądać z powodu felietonów, w większości nie porzuciły mnie z powodu zalewu treści technicznych. Czasem nawet, na zachętę, coś polubiły.
  • Moje (siedmiomiesięczne dziś) dziecko w międzyczasie nauczyło się raczkować i wstawać. Za każdym razem, kiedy próbowało wkładać do buzi kabel USB podpięty do komputera, udało mi się odciągnąć je w porę 😉

Co mnie zasmuca

  • Wykorzystałam tylko połowę z technologii z mojej listy.
  • W szczególności, o zgrozo i wstydzie, nie mam testów.
  • Projekt nie jest gotowy! Ani przez chwilę nie zakładałam, że będzie, ale apetyt rośnie w miarę jedzenia.

Co dalej?

Podobno gala z wręczeniem nagród!

A ja powoli wracam do pracy zarobkowej.

Nie zamierzam porzucać projektu, ale na pewno będę blogować o niem trochę rzadziej.

Lista moich wpisów konkursowych

  1. Dam się poznać: mój projekt open source
  2. No i Git! Kontrola wersji służy nie tylko programistom
  3. Git w Eclipse
  4. Git: przedzieram się przez gałęzie
  5. 3.. 2.. 1.. Witaj, Spring Boot!
  6. A więc do tego służy git rebase!
  7. Konkurs „Daj się poznać”: status i wrażenia
  8. Na co komu Maven?
  9. HTML5 na pomoc, czyli Firefox ssie
  10. Odkrywam nową Javę przy okazji dat
  11. Mobilna wersja strony w Spring Boot
  12. Wdrażam moją aplikację (która nadal nic nie robi) w chmurze Pivotal Web Services
  13. Historie komórkowe
  14. Czasami coś idzie nie tak… Moja własna strona błędu
  15. Pozbywam się powtarzalnych fragmentów HTML przy użyciu Thymeleaf
  16. Spring Boot: Bezpieczeństwo 101
  17. Własny formularz logowania Spring Security
  18. Jak zacząć z MongoDB?
  19. MongoDB + Spring Data
  20. Jak zabezpieczyć strony generowane przez Spring Boot Actuator?
  21. Zapisuję dane użytkowników w bazie i robię głupie błędy podczas walidacji formularza
  22. Baza danych w chmurze Pivotal (photo story)
  23. Jak wysłać maila z aplikacji webowej w Javie?
  24. Aktywacja konta poprzez email

 

Aktywacja konta poprzez email

Poprzedni wpis poświęciłam kwestii wysyłania maili przez aplikację napisaną w Javie.

Kontynuując, teraz chcę upewnić się, że świeżo zarejestrowany użytkownik to człowiek (a nie bot) i że podał poprawny adres email. Poniżej opisuję kroki prowadzącego do tego celu.

Krok 1: nieaktywny użytkownik z wygenerowanym kodem

Po wypełnieniu formularza rejestracyjnego obiekt reprezentujący użytkownika jest zapisywany w bazie danych jako nieaktywny. Razem z nim zapisywany jest losowo wygenerowany kod do aktywacji konta. Na tym etapie użytkownik nie może się logować, nie zna też przypisanego mu kodu.

Krok 2: email z linkiem aktywacyjnym

Aplikacja wysyła na adres podany przez użytkownika wiadomość zawierającą link aktywacyjny. Kod wygenerowany losowo w poprzednim kroku jest doklejony do adresu jako parametr URL.

Krok 3: obsługa linku aktywacyjnego

Wejście pod adres podany w mailu powoduje uruchomienie servletu weryfikacyjnego. Aplikacja szuka w bazie danych obiektu użytkownika, któremu przypisano kod podany jako parametr. Jeśli taki użytkownik istnieje, jest oznaczany jako aktywny. Od tej chwili może się już logować, ponieważ potwierdzono (czy raczej zwiększono prawdopodobieństwo), że to rzeczywista osoba.

Krok 4: usunięcie nieaktywnych kont

Należałoby usunąć zbyt długo przechowywane nieaktywne konta. Ale jeszcze się za to nie zabrałam 🙂

Ciekawostka: wyszukanie użytkownika w bazie

Oto ilość pracy, którą włożyłam w wyszukanie użytkownika w bazie danych MongoDB po wartości confirmationId:

Resztę robi za mnie Spring Data, parsując nazwę metody!

Więcej informacji na ten temat można znaleźć tutaj.

PS.

To ostatni techniczny wpis w ramach konkursu Daj się poznać 2016. Jutro postaram się napisać krótkie podsumowanie. O ile wieczorem na spotkaniu młodych mam nie poleje się za dużo wina.

 

Jak wysłać maila z aplikacji webowej w Javie?

Po co mi maile?

Warunkiem utworzenia konta użytkownika jest podanie poprawnego adresu email, do którego dany użytkownik rzeczywiście ma dostęp.

W kolejnym wpisie omówię dokładniej proces weryfikacji. Na teraz istotne jest to, że moja aplikacja musi być w stanie wysłać do użytkownika maila z linkiem do uaktywnienia konta.

Jak to zrobić?

Korzystam z frameworka Spring Boot (czyli właściwie z nakładki ułatwiającej korzystanie z frameworka Spring zgodnie z zasadą „konwencja ponad konfiguracją”).

Kroki do realizacji mojego celu są następujące:

1. Dodaję zależność w pliku pom.xml:

2. Piszę kod tworzący i wysyłający wiadomości:

3. Ustawiam właściwości aplikacji

Co może pójść nie tak?

com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first.

Koniecznie jest nadanie wartości true właściwości spring.mail.properties.mail.smtp.starttls.enable, a najlepiej całej trójce kończącej powyższy listing.

javax.mail.AuthenticationFailedException: 534-5.7.9 Application-specific password required.

To akurat sprawa typowa dla serwisu GMail. Jeśli chcesz, żeby aplikacja łączyła się z kontem email i w Twoim imieniu wysyłała maile, musisz stworzyć specjalne hasło aplikacji, różne od hasła prywatnego. W każdej chwili możesz podejrzeć listę aplikacji, które korzystają z takiego hasła (na stronie https://security.google.com/settings/security/apppasswords), a także dowolną z nich zablokować.

Google uprzejmie zwraca uwagę, że nadal nie jest to najbezpieczniejsze rozwiązanie.

com.sun.mail.util.MailConnectException: Couldn’t connect to host.

Ten błąd pojawił się, kiedy przełączyłam konto z GMaila na adres email na mojej wirtualnej instancji obsługującej kilka domen. Początkowo myślałam, że Java Mail nie chce pracować z „wirtualnymi” adresami obsługiwanymi przez serwer SMTP w innej domenie. Ostatecznie okazało się jednak, że przyczyną błędu był nadmiarowy biały znak w pliku application.properties 🙂

Ciekawostka: tymczasowe maile

Wiele aplikacji wymusza na użytkowniku podanie działającego adresu email. Główne powody to:

  • zwiększenie prawdopodobieństwa, że mamy do czynienia z człowiekiem, a nie robotem,
  • umożliwienie spamowania 🙂
  • zabezpieczenie użytkownika przed literówką w adresie email, która mogłaby doprowadzić nawet do utraty konta w aplikacji (brak możliwości zresetowania hasła bez działającego adresu).

Wiele osób tworzy sobie dodatkowe konto email, które używane jest do rejestracji w mniej istotnych serwisach (sama mam osobne konto do zakupów internetowych). Zdarza się jednak, że adresu email żąda strona lub aplikacja, z którą na pewno już nigdy nie będziesz mieć nic wspólnego. Co wtedy zrobić?

Możesz założyć tzw. jednorazowe (ang. disposable) konto email, np. w serwisie Guerilla Mail. Poczta, która tam trafia, jest przechowywana przez godzinę niezależnie od tego, czy ktoś ja odczyta, czy nie.

Ocenę moralną pozostawiam Czytelnikowi.

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

 

Milsza strona programowania