Pokazywanie postów oznaczonych etykietą java. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą java. Pokaż wszystkie posty

wtorek, 27 stycznia 2009

Unable to find valid certification path to requested target

--------------------------

Unable to find valid certification path in English

--------------------------

Jeśli nawiązujecie połączenie z usługą sieciową (web service) poprzez https (czyli z użyciem SSL) natrafić możecie na dość znany wyjątek:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target


Oznacza to tyle, że łączymy się z usługą przez SSL i nie znamy lub nie akceptujemy certyfikatu uwierzytelniającego tej usługi. Gdybyśmy łączyli się ze stroną www przez przeglądarkę, wyskoczyłoby nam znane okienko informujące nas, że połączenie nie może być nawiązane, gdyż (cytując Firefox'a) "certyfikat nie jest zaufany, ponieważ certyfikat wystawcy nie jest zaufany.". Wtedy najczęściej klikamy "Zaufaj witrynie" lub "Potwierdź wyjątek bezpieczeństwa" i już możemy oglądać naszą witrynę w bezpiecznym połączeniu SSL.

W przypadku programów napisanych w języku Java, nie mamy pewności, że zawsze po drugiej stronie aplikacji będzie siedział użytkownik, który kliknie "Potwierdź wyjątek bezpieczeństwa". Dlatego, aby aplikacja łączyła się zawsze poprawnie przez SSL, należy wcześniej ten "wyjątek" potwierdzić.
Najprostszym sposobem jest wygenerowanie tak zwanego keystore'a dla danego certyfikatu i potem użycie go w aplikacji. Robi się to w sposób następujący:

1. wygenerowanie pliku .cer

Jest to plik reprezentujący certyfikat. Często jest udostępniany przez firmę, z której serwerem łączymy się przez SSL. Jeśli jednak nie mamy do niego dostępu najprościej go zdobyć wchodząc na dany adres usługi (ten adres uslugi, z która później ma łączyć się nasza aplikacja) poprzez dowolną przeglądarkę. Potwierdzić certyfikat, następnie odnaleźć go w ustawieniach przeglądarki i na końcu wyeksportować do pliku .cer. Większość przeglądarek (na pewno Firefox i Opera) udostępniają taką funkcjonalność.

2. wygenerować keystore

Keystore możemy wygenerować wraz z narzędziem keytool, które dostępne mamy zawsze wraz z naszym JDK. Wywołujemy ten program w konsoli w sposób następujący:

keytool -import -trustcacerts -keystore cacerts -storepass twoje_haslo -noprompt -file cert.cer

gdzie: -file to wygenerowany w punkcie pierwszym plik .cer, -keystore to nazwa pliku keystore (może być dowolna), -storepass to hasło które będzie używane wraz z danym keystorem.

3. ustawienie propertiesów

Na końcu należy wygenerowany plik keystore (w naszym przypadku nazywa się cacerts) wrzucić do katalogu naszej aplikacji, a w samym kodzie ustawić dwa dodatkowe propertiesy:


System.setProperty("javax.net.ssl.trustStore", "cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "twoje_haslo");


Od tego momentu powinniśmy bez problemów łączyć się z dowolną bramką webservicową (czy też jakąkolwiek usługą w sieci) poprzez SSL.

Referencje:
http://wso2.org/forum/thread/3018
http://blogs.sun.com/andreas/entry/no_more_unable_to_find

poniedziałek, 25 sierpnia 2008

java.util.Date vs java.sql.Date

Natknąłem się na ciekawe rozwiązanie architektoniczne w JDBC: obiekt java.sql.Date. Jest to obiekt, który dziedziczy bezpośrednio po java.util.Date, pozwalający nam mapować w naszych beanch pola typu Date z naszych baz danych.
Czy wszystko jest takie piekne? Nie do końca, gdyż w naszych beanach zwykle trzymamy obiekty typu java.util.Date.
Wyboraźmy sobie prosty przykład:

Mamy klase User w postaci

public class User
{
protected String userName;
protected Date birthDate; // tutaj java.util.Date czyli standard

//odpowiednie gettery i settery
}
Posiadamy również odpowiadającą jej tabelę w bazie danych:

CREATE TABLE Users (
userName VARCHAR2(150),
birthDate DATE );

Sytuacja jak najbardziej standardowa. Teraz jeśli wyciągnięte dane z bazy (przy pomocy ResultSeta) dodamy do naszego beana:

User u = new User();
u.setUserName(rSet.getString(1));
u.setBirthDate(rSet.getDate(2));

nic się nie stanie. W prawdzie metoda User.setBirthDate oczekuję java.util.Date, a ResultSet.getDate zwraca java.sql.Date, to jednak wszystko jest w porządku, gdyż java.sql.Date dziedziczy po java.util.Date. Jaja zaczynają się, gdy informacje chcemy ponownie przekazać do bazy danych.
Teraz gdy updateujemy jakies informacje przy pomocy jakies procedury skladowanej czy poprzez wywolanie SQL'owego UPDATE, zawsze ale to zawsze poprosza nas o Date w postaci java.sql.Date. Czyli kod
statement.setDate(2,user.getBirthDate());
sie po prostu nie skompiluje. Rzutowanie nic tu nie da, gdyż przykładowo poniższy kod:
user.setBirthDate(new Date());
statement.setDate(2,(java.sql.Date)user.getBirthDate()); // tu exception
się poprostu wywali przy pierwszym wywołaniu. Trzeba utworzyć explicite obiekt java.sql.Date, ale żeby nie było za prosto, nie ma żadnego konstruktora typu public Date(java.util.Date utilDate). Nie, przecież to by było zbyt proste. Zamiast tego musimy zrobic new java.sql.Date(utilDate.getTime()). Konstruktor java.sql.Date przyjmuje longa jako liczbę milisekund od roku 1970, a metoda getTime z java.util.Date taka informacje zwraca. Wszystko działa okej, ale czy na pewno to jest dobre rozwiązanie? Szczerze mówiąc, ja nie wiem. Skoro sami ludzie z Sun Microsystems mówią, że java.sql.Date to tylko "thin wrapper", to po co w ogóle został wprowadzony? Czy istnieje jakiś lepszy (w sensie nie polegający na roku 1970) sposób na konwertowanie dat z java.util na java.sql? Googlałem pobieżnie, nie znalazłem. Może ktoś zna?

poniedziałek, 23 czerwca 2008

Migracje bazodanowe: czego w Javie nie znajdziemy?

Czytam sobie sporo o Railsach ostatnio. Na tyle mi się framework spodobał (i język Ruby też), że chwilowo nawet myślałem o całkowitej migracji Java -> Ruby & Rails. Ponieważ jednak firm RoR'owych u nas we Wrocławiu jak na lekarstwo (w sumie tylko jedna, pracowników serdecznie pozdrawiam :) ), to ostatecznie pomysł ten zarzuciłem. Nie zmienia to faktu, że czytanie książek i blogów o Railsach sporo mi dało. Inaczej teraz trochę patrze na programowanie, jest też kilka spraw, które w frameworkach Javowych bardzo mi od tego czasu brakuje.
Jednym z takich elementów są migracje (ang. migrations), potężne narządzie frameworka Ruby on Rails.
W Javie pisząc aplikacje wykorzystujące bazę danych przyzwyczailiśmy się już do frameworków tzw. mapujących. To znaczy takich, w których model biznesowy jest mocno zintegrowany z bazą danych. Boli nas trochę fakt, że gdy tworzymy aplikacje z wykorzystaniem jakiegoś narzędzia ORM'owego, musimy wyspecyfikować:

- pole w modelu
- getter
- setter
- mapowanie w kierunku a->b
- mapowanie w kierunku b->a
- definicje kolumny

Oczywiście nie można dramatyzować: model (a wiec odpowiednie fieldy, gettery, settery) pomoże nam stworzyć nasze ulubione IDE, a najnowsze frameworki (z wykorzystaniem adnotacji) umiejscawiają nam definicje bazodanowe w samym modelu. Wszystko robi się niby DRY (Don't Repeat Yourself), wszystko wydaje się być w jednym miejscu i wszelkie zmiany w projekcie można wprowadzać bezboleśnie. Zapominamy tutaj o jednej ważnej sprawie - danych w bazie danych.

Możemy wyobrazić sobie, że po miesiącu czy dwóch naszego developingu, powstaje aplikacja/prototyp, która zostaje wdrożona u klienta. Schemat bazy danych jest oczywiście generowany z naszego modelu na bazie produkcyjnej u klienta. Czysta, nowa, świeżutka baza wraz z aplikacją zostaje uruchomiona. My zabieramy się za kolejną iterację, a klient wdraża się w jej pierwszą wersję. W między czasie dodajemy nowe kolumny, zmieniamy nazwy tabel, łączymy pewne rzeczy w jedną całość - jesteśmy bardzo agile - nie boimy sie zmian. Przychodzimy do klienta, chcemy mu wygenerować nowa bazę a on nam mówi "Hola hola, ja tu już mam pewne dane, nie chce ich tracić". Po chwili namysłu dochodzimy do wniosku, że możemy wygenerować plik SQL, w którym umieścimy wszystkie zmiany w nowej bazie danych względem tej starej (znany wszystkim DDL, czyli alter table i takie tam) i tak gotowy skrypt uruchomimy po stronie bazy produkcyjnej.

Ok, rozwiązanie niby dobre. Spotkałem się z nim w praktyce, pliki takie nazywane były "patchami" (moim zdaniem nazwa dobra) i tworzone były dla każdej kolejnej iteracji przygotowywanej aplikacji. Trzymane wraz z kodem w repozytorium nie miały prawa zaginąć. Teraz tworząc pole String nickname w modelu User musiałem dodatkowo w patchu patch_078.sql umieszczać wpis "alter table add column nickname varchar". W momencie integracji wersji deweloperskiej z produkcyjną, po wprowadzeniu zmian w kodzie uruchamiany zostawał patch i schemat bazy był aktualny z modelem. Wszystko niby fajnie, ale właśnie tu zaczynają się schody. Po pierwsze przestaje mieć pojedyńczej informacji (w tym przypadku informacji o schemacie bazodanowym) w jednym miejscu - co szybko prowadzić może do pomyłek. Druga sprawa to brak powiązania tych zmian z kodem na repozytorium. Wprowadzę zmiany w kodzie, commitne, mnie trochę czasu, update'uje patch na repozytorium. Mijają cztery dni i okazuje się, że muszę cofnąć zmiany w moim modelu. Muszę PAMIĘTAĆ, że odpowiednie zmiany powinny być update'owane w patchu (rollback na repozytorium mi tego nie zapewni). Ból, ból, ból i zgrzytanie zębów.

O wiele ciekawiej rozwiązane jest to w Railsach. Patche (tutaj zwane migracjami) tworzone są za nas automatycznie. Taki patch zawiera nie tylko metodę updatującą schemat bazy, ale również wycofującą te zmiany. W ogólności każda migracja to klasa Ruby'ego implementująca dwie metody up oraz down. Metoda up wprowadza zmiany w schemacie bazy danych, metoda down je wycofuje. Przez co uzyskujemy patch działający w dwie strony. Zestaw operacji jakie możemy wykonać jest dowolny, możemy wstawiać dane, zmieniać strukturę schematu (z użyciem predefiniowanych metod podobnych do tych które znamy z DDL, czyli create/drop table, add/remove column i tak dalej). Każda taka migracja ma przypisany swój numer porządkowy, a sam schemat trzyma informacje o swojej wersji. Jeśli zatem przykładowo mamy schemat bazy danych w wersji 004, a w czasie developingu pojawiły się dwie nowe migracje 005 oraz 006, to wywolanie komendy rake migrate spowoduje, że framework wywoła w kolejności metody up, najpierw z migracji 005, a następnie z migracji 006,a na końcu zmieni wersję schematu na 006. Jeśli kiedykolwiek trzeba będzie sie cofnąć w zmianach schematu, na przykład do wersji 004, wykonane zostaną kolejno metody down w migracji 006, a poźniej w 005. Piękna sprawa!

Klasy migracji są tworzone dla modeli automatycznie, więc nie ma obawy, że do repozytorium nie zostaną przekazane razem. Wtedy cofnięcie się w ramach repozytorium do kodu z przed na przykład kilku dni nie stanowi problemu.

O migracjach w Rails można by mówić wiele, fakt faktem brakuje mi czegoś takiego w frameworkach Javowych. Słyszałem, że jakieś OMR'y generują patche dla konkretnej bazy danych w oparciu o zmiany jakie zaszły w modelu. Jakie? Nie wiem, może wy mi powiecie, jestem bardzo ciekaw. Wtedy byłoby już fajnie, chociaż wciąż nie pozostawalibyśmy DRY, ale przynajmniej były to już jakiś krok w dobrym kierunku.

Orginał tego postu wyglądał ciutkę inaczej od tego co tu przeczytałeś, ale durny siersciuch (kot) gonił muchę po mieszkaniu, przebiegł się po klawiaturze, skasował sporą część zaznaczonego tekstu, a blogspot automatycznie po kilku sekundach zapisał zmiany. 40% tekstu jest odtworzone, a więc podatne na błędy i gorsze jakościowo - nie cierpie pisać czegoś dwa razy.

Z kota zrobie jutro gyros.


update:
jeśli ktoś zna jakiś framework, który rzeczywiście na podstawie schematu bazy i modelu, jest w stanie wygenerować skrypt sql ze zmianami jakie należy wprowadzić w bazie, niech da mi prosze znać na maila lub w komentarzach. W JPA szukałem i się nie doszukałem.