niedziela, 23 maja 2010

Pierwsze próby z JNDI i DataSource

Witam!

Ostatnio wspominałem, że zacząłem swoją zabawę z Java Enterprise Edition. Aplikacja, którą piszę ma za zadanie komunikację z bazą danych i wyświetlać z niej wyniki. Nie było by problemu, gdybym w czasie tworzenia aplikacji korzystał z jednej bazy, a tak korzystam z trzech - lokalnej, na wirtualnym serwerze oraz znajdującej się na serwerach uczelnianych. Z początku po prostu zmieniałem parametry przy połączeniu via JDBC, ale takie rozwiązanie mnie nie satysfakcjonowało. Poczytałem trochę, popytałem i dowiedziałem się, że idealnym rozwiązaniem było by JNDI + DataSource. W sieci można znaleźć sporo informacji na ten temat (szczególnie w kontekście Springa, którym zajmę się dopiero za dłuższy czas), ale ja szukałem totalnego minimum i świadomości jak to działa. Z pomocą przyszedł Paweł Cegła, który pokazał mi praktyczne zastosowanie JNDI i DS w projekcie. Teraz krótko (za Pawła zgodą) opiszę jak najprościej i najszybciej skonfigurować naszą aplikację, aby uzyskać dostęp do bazy danych, a w przypadku zmian parametrów ograniczało się to zaledwie do edycji jednego pliku.

Na początek tworzymy nowy projekt: File -> New -> Dynamic Web Project. Ja nadałem projektowi nazwę "JNDI_and_DS" i moja konfiguracja wygląda następująco:

Po stworzeniu projektu ma on następującą strukturę:

Dane konfiguracyjne aplikacji webowej umieszczamy w katalogu WebContent -> META-INF. Należy w nim utworzyć plik XML i nazwać go dokładnie context.xml:

Konfiguracja kontekstu aplikacji (bo o nim mowa) w najprostszej postaci wygląda następująco:

Jak widać składa się na nią kilka elementów:
  1. całość jest zawarta między znacznikami Context z parametrem path określającym ścieżkę (tzw. context path) do naszej aplikacji (należy pamiętać o znaku '/'!)
  2. pojedynczy zasób jest określony w obrębie znaczników Resource i posiada liczne atrybuty:
  • name - na podstawie tej nazwy będziemy wyszukiwać naszą konfigurację w kontekście aplikacji
  • auth - tu do wyboru mamy "Application" lub "Container". Ta druga opcja wskazuje, że w imieniu naszej aplikacji to kontener, w którym jest uruchamiana będzie zarządzał połączeniami z bazą danych i całym DataSource
  • type - ty wpisujemy jakiego typu zasób będziemy pobierać z kontekstu aplikacji odwołując się do jego nazwy (my chcemy DataSource, więc wpisujemy javax.sql.DataSource)
  • maxActive - maksymalna ilość połączeń w obrębie DataSource z bazą danych. Musi zgadzać się z konfiguracją samej bazy danych
  • maxIdle - maksymalna ilość tzw. uśpionych połączeń. Temat omówię w dalszej części posta
  • maxWait - ilość czasu (w ms), jaką maksymalnie aplikacja oczekiwać będzie na odpowiedź serwera baz dancyh
  • username - nazwa użytkownika w systemie baz danych
  • password - hasło wyżej wymienionego użytkownika
  • driverClassName - pełna ścieżka do pliku sterownika, za pomocą którego będzie wykonywane połączenie (ja korzystam z biblioteki JDBC i bazy MySQL, więc sterownik ten znajduje się w com.mysql.jdbc.Driver)
  • url - ścieżka dostępu do naszej bazy danych (wraz z jej nazwą)

Jeśli wypełnimy poprawnie wszystkie te pola, możemy przystąpić do napisania servletu, który za pomocą DataSource będzie komunikować się z naszą bazą danych. Ja przyjąłem wariant, w którym stworzyłem servlet, z którego będą dziedziczyć wszystkie servlety współpracujące z bazą danych, więc sam obiekt DataSource jest polem tejże klasy, a w metodzie init wyszukuję w kontekście aplikacji DataSource. Jak to mówią - raz a porządnie :)

Wygląda to mniej więcej tak:


public void init(ServletConfig config) throws ServletException {
super.init();

try {
InitialContext context = new InitialContext();
dataSource = (DataSource) context
.lookup("java:/comp/env/jdbc/JNDI_and_DS");
} catch (NamingException e) {
throw new ServletException("Problem z JNDI i DS");
}
}

Dziwne może wydawać się może, że szukamy referencji do DataSource (metoda lookup) pod inną nazwą niż ustawiliśmy w konfifuracji. Otóż na serwerze Tomcat 6, kontekst aplikacji znajduje się pod adresem "java:/comp/env/". Niestety jest to inna ścieżka na różnych serwerach aplikacji.

Do pełni szczęścia brakuje nam już tylko dodanie biblioteki MySQL Connector/J (wersja JDBC ze sterownikiem do baz danych MySQL) do katalogu lib w katalogu, w którym znajduje się serwer Tomcat 6 i już!

W przypadku aplikacji napisanej przeze mnie (link do projektu Eclipse zamieściłem na końcu posta) poprawna konfiguracja i uruchomienie aplikacji objawia się takim komunikatem:

Cały czas wspominam o DataSource, ale wciąż nie wytłumaczyłem co to tak naprawdę jest. Otóż jest to obiekt, który w naszym imieniu przechowuje pulę połączeń (tzw. connection pooling) oraz pomaga w procesie transakcji. W przypadku naszej prostej aplikacji daje nam to ten komfort, że jesteśmy w stanie określić wszystkie parametry związane z połączeniami oraz dochodzi element związany z wydajnością. W momencie kiedy kontener tworzy obiekt DataSource, przypisuje mu kilka tzw. uśpionych połączeń (idle connections). Dzięki temu, kiedy nasza aplikacja chce połączyć się z bazą danych, połączenie nie jest tworzone (a jest to proces dosyć czasochłonny, szczególnie w MySQL'u), a jedynie pobierane z puli i udostępniane. Kiedy kończymy pracę z danym połączeniem wraca ono do puli i czeka na ponowne użycie.

A teraz kilka linków:

Mam nadzieję, że wpis się komuś przyda :) Zachęcam do komentowania i do usłyszenia w kolejnym poście!

3 komentarze:

  1. Po dyskusji z Mateuszem dokonałem małej zmiany w wyjaśnieniu czym jest DataSource. Trochę się zapędziłem i przekazałem to nie tak jak trzeba. Mam nadzieję, że tym razem jest wszystko dobrze.

    OdpowiedzUsuń
  2. Zastanawia mnie, dlaczego nie skorzystałeś z adnotacji @Resource, aby mieć dostęp do danych, zamiast go odszukiwać w JNDI?

    OdpowiedzUsuń
  3. Bo mało wiem o adnotacjach i nie wiedziałem, że tak można :) Widać wreszcie trzeba będzie zgłębić temat. Dzięki Jacku :)

    OdpowiedzUsuń