Taki sukces wymagał odpowiedniego rozgłosu! Musiał być oznajmiony w odpowiedni sposób - przepisem na przykładową aplikację JPA z Eclipse IDE, GlassFish i Firebird.
Zainstalowawszy WTP 2.0M5 (z narzędziami do JPA i JSF) oraz GlassFish Eclipse Plugin postanowiłem popróbować się z utworzeniem prostej aplikacji JPA, która korzystałaby ze źródła danych w GlassFish v2 do Firebird v2 (temat pojawił się na grupie pl.comp.lang.java - EJB3 SunAppServer i Firebird - i akurat zbiegł się z moim powrotem do lektury specyfikacji JPA). Nigdy nie pracowałem z Firebird, ale poza poznaniem podstawowych komend do uruchomienia bazy danych i jej zatrzymania nie zamierzałem poznawać produktu więcej. To jest właśnie zaleta warstwowych aplikacji, gdzie część odpowiedzialności za tworzenie aplikacji można scedować na inne osoby w projekcie (no cóż, nie pierwszy raz występuję w kilku rolach: administratora bazy danych, serwera aplikacyjnego i programisty). W zasadzie nie potrzebowałem serwera aplikacyjnego do uruchomienia aplikacji z JPA, ale zadane pytanie na p.c.l.java dotyczyło takiego środowiska, a co więcej zarządzanie połączeniami do bazy danych zlecimy serwerowi aplikacyjnemu GlassFish (poznanie kilku komend administracyjnych nie powinno przysłonić właściwej treści wpisu).
Zanim przedstawię przepis na aplikację JPA i otaczający ją świat, kilka słów o wersjach oprogramowania w środowisku wykonawczym (uruchomieniowym):
Poniższy obrazek mówi sam za siebie - Eclipse IDE 3.3 z wtyczkami, które obiecują usprawnić pracę z technologiami Java EE 5 i przybliżyć nas do (bądź wręcz przewyższyć możliwości) NetBeans IDE 6.0 (jeszcze nie tym razem ;-))
Krok 1. Definicja GlassFish v2 w Eclipse IDE 3.3
Rozpoczynam od definicji GlassFish w Eclipse IDE.
Jak widać powyżej lista dostępnych serwerów aplikacyjnych jest imponująca, ale tym razem padło na GlassFish (reszta musi poczekać).
Po chwili Eclipse IDE już wie jak pracować z GlassFish v2. Na razie idzie gładko. GlassFish jest zdefiniowany i uruchomiony - wszystko z poziomu Eclipse IDE.
Krok 2. Uruchomienie Firebird Superserver
Uruchamiam Firebird Superserver (zalecana konfiguracja na MS Windows, na którym pracuję).
$ cd $FIREBIRD_HOME, co tworzy dwa serwisy Firebird Guardian oraz Firebird Server - oba powinny już być uruchomione.
$ ./bin/install_super.bat
Po tym można już założyć nową bazę danych dedykowaną dla mojej przykładowej aplikacji.
$ cd $FIREBIRD_HOME
$ ./bin/isql.exe
Use CONNECT or CREATE DATABASE to specify a database
SQL> create database 'c:\temp\glassfish-jpa.fdb' page_size 8192
CON> user 'SYSDBA' password 'masterkey';
SQL> exit;
I sprawdzam, że baza danych faktycznie jest stworzona i dostępna z zewnątrz.
$ ./bin/isql.exe
Use CONNECT or CREATE DATABASE to specify a database
SQL> connect 'localhost:c:\temp\glassfish-jpa.fdb' user 'sysdba' password 'masterkey';
Database: 'localhost:c:\temp\glassfish-jpa.fdb', User: sysdba
SQL> exit;
Dowiaduję się o możliwości utworzenia synonimów (ang. alias) dla nowoutworzonej bazy danych. Korzystanie z synonimu jest prostsze i dodatkowo skrypt tworzący pulę połączeń w GF nie akceptuje dwukropka w ścieżce bazy danych (':'), który jest separatorem w opcji --property.
W pliku $FIREBIRD_HOME/aliases.conf umieściłem poniższą linię:
glassfish-jpa=c:\temp\glassfish-jpa.fdb
Krok 3. Definicja źródła danych do Firebird w GlassFish
Postępujemy zgodnie z dokumentacją GlassFish'a - Chapter 14: Using the JDBC API for Database Access.
Istnieją dwie możliwości zdefiniowania źródła danych w GlassFish:
- Polecenie asadmin create-jdbc-connection-pool
- Konsola administracyjna pod adresem http://localhost:4848/
Najpierw należy przekopiować sterownik JDBC Firebird'a - jaybird-full-2.1.1.jar - do katalogu $GLASSFISH_HOME/domains/domain1/lib/ext.
Następnie wydać polecenia z poziomu konsoli:
$ ./bin/asadmin.bat create-jdbc-connection-pool \
--datasourceclassname org.firebirdsql.jdbc.FBWrappingDataSource \
--restype javax.sql.DataSource \
--property password=masterkey:userName=sysdba:databaseName=//localhost/glassfish-jpa:pooling=true:minSize=5:maxSize=30:type=type4 \
FirebirdPool
$ ./bin/asadmin.bat ping-connection-pool FirebirdPool
Command ping-connection-pool executed successfully.
$ ./bin/asadmin.bat create-jdbc-resource --connectionpoolid FirebirdPool jdbc/Firebird
W konsoli można sprawdzić, że połączenie faktycznie zostało zdefiniowane, a jak później się okaże i dostępne dla aplikacji.
Krok 4. Utworzenie encji Customer
Mając zainstalowaną i co ważne działającą wtyczkę Eclipse Dali skrzętnie korzystam z jego pomocy przy utworzeniu encji.
, a następnie
Po chwili mam kompletną klasę encji, która jest o tyle ciekawa, że jak się później okazało wymagała adnotacji wcześniej przeze mnie nie wykorzystywanych - @NamedQuery oraz @Column z atrybutem columnDefinition. Użycie @NamedQuery było opcjonalne i służyło jedynie upraszczeniu zarządzania zapytaniem (możliwe późniejsze modyfikacje) podczas, gdy użycie z @Column(columnDefinition="NUMERIC(10)") było konieczne dla poprawnego działania TopLink z Firebird. TopLink jest domyślnym środowiskiem JPA w GlassFish i niestety nie posiada wsparcia dla Firebird, a domyślna konfiguracja TopLink wskazuje na Oracle i stąd pewne rozbieżności w tworzonych komendach SQL.
package pl.jaceklaskowski.jpa.firebird.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
@Entity
@NamedQuery(name = "znajdzWszystkichKlientow", query = "SELECT c FROM Customer c")
public class Customer {
private int pesel;
private String name;
public Customer() {
}
public Customer(int pesel, String name) {
this.pesel = pesel;
this.name = name;
}
@Id
@Column(columnDefinition = "NUMERIC(10)")
public int getPesel() {
return pesel;
}
public void setPesel(int pesel) {
this.pesel = pesel;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Do pełnej konfiguracji encji Customer potrzebny był jeszcze plik konfiguracyjny - persistence.xml.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="firebird">
<jta-data-source>jdbc/Firebird</jta-data-source>
<class>pl.jaceklaskowski.jpa.firebird.entities.Customer</class>
<properties>
<property name="toplink.ddl-generation" value="drop-and-create-tables"/>
<property name="toplink.logging.level" value="ALL"/>
</properties>
</persistence-unit>
</persistence>
Krok 5. Utworzenie aplikacji internetowej - firebird
Stworzyłem bardzo trywialny servlet, który podlega zarządzaniu zależnościami w Java EE 5 i mogłem skorzystać z adnotacji.
package pl.jaceklaskowski.jpa.firebird.servlets;
import java.io.IOException;
import java.util.List;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.UserTransaction;
import pl.jaceklaskowski.jpa.firebird.entities.Customer;
@SuppressWarnings("serial")
public class CreateCustomerServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
@PersistenceContext(unitName = "firebird")
private EntityManager em;
@Resource
private UserTransaction ut;
@SuppressWarnings("unchecked")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int pesel = Integer.parseInt(request.getParameter("pesel"));
String name = request.getParameter("name");
if (pesel > 0 && name != null) {
Customer customer = new Customer(pesel, name);
try {
ut.begin();
em.persist(customer);
ut.commit();
} catch (Exception e) {
throw new ServletException(e);
}
}
List<Customer> customers = em.createNamedQuery("znajdzWszystkichKlientow").getResultList();
request.setAttribute("customers", customers);
request.getRequestDispatcher("/customers.jsp").forward(request, response);
}
}
z poniższym deskryptorem instalacji aplikacji internetowej - WEB-INF/web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee web-app_2_5.xsd" version="2.5">
<display-name>firebird-web</display-name>
<servlet>
<description></description>
<display-name>CreateCustomerServlet</display-name>
<servlet-name>CreateCustomerServlet</servlet-name>
<servlet-class>pl.jaceklaskowski.jpa.firebird.servlets.CreateCustomerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CreateCustomerServlet</servlet-name>
<url-pattern>/createCustomer</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>customers.jsp</welcome-file>
</welcome-file-list>
</web-app>
Pozostało stworzyć stronę prezentującą wyniki działania aplikacji - customers.jsp:
Krok 6. Uruchomienie aplikacji
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Klienci</title>
</head>
<body>
<form action="createCustomer">
<table>
<tr>
<td>PESEL:</td>
<td><input name="pesel" /></td>
</tr>
<tr>
<td>Nazwa:</td>
<td><input name="name" /></td>
</tr>
<tr>
<td><input type="submit" value="Stwórz" /></td>
</tr>
</table>
</form>
<table border="0" cellpadding="1" cellspacing="1">
<tr>
<td>Lp.</td>
<td>PESEL</td>
<td>Nazwa</td>
</tr>
<c:forEach var="customer" items="${customers}" varStatus="status">
<tr>
<td>${status.count}</td>
<td>${customer.pesel}</td>
<td>${customer.name}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
Wybieram menu kontekstowe Run As->Run on Server:
i po chwili w zakładce konsoli pojawiają się nieciekawe, ale oczekiwane komunikaty o uruchomieniu aplikacji:
Buildfile: C:\.eclipse\jpa\.metadata\.plugins\org.eclipse.jst.server.generic.core\serverdef\sunappsrv-ant.xml
deploy.j2ee.web:
[echo] C:/.eclipse/jpa/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/firebird-web
[jar] Building jar: C:\.eclipse\jpa\.metadata\.plugins\org.eclipse.jst.server.generic.core\serverdef\firebird-web.war
tools:
deploy:
[exec] Command deploy executed successfully.
deploy-url-message:
[echo] Application Deployed at: http://127.0.0.1:8080/firebird-web
BUILD SUCCESSFUL
Total time: 8 seconds
a w dzienniku zdarzeń GlassFish'a:
[#|2007-02-26T08:18:15.546+0100|INFO|sun-appserver9.1|oracle.toplink.essentials.session.file:/C:/apps/glassfish/domains/domain1/applications/j2ee-modules/firebird-web/WEB-INF/lib/f
irebird.jar-firebird|_ThreadID=14;_ThreadName=Thread-24;|Not able to detect platform for vendor name [Firebird 2.0*WI-V2.0.0.12748 Firebird 2.0/tcp (dev)/P10]. Defaulting to [oracl
e.toplink.essentials.platform.database.DatabasePlatform]. The database dialect used may not match with the database you are using. Please explicitly provide a platform using proper
ty toplink.platform.class.name.|#]
[#|2007-02-26T08:18:15.562+0100|CONFIG|sun-appserver9.1|oracle.toplink.essentials.session.file:/C:/apps/glassfish/domains/domain1/applications/j2ee-modules/firebird-web/WEB-INF/lib
/firebird.jar-firebird.connection|_ThreadID=14;_ThreadName=Thread-24;|connecting(DatabaseLogin(
platform=>DatabasePlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))|#]
[#|2007-02-26T08:18:15.562+0100|CONFIG|sun-appserver9.1|oracle.toplink.essentials.session.file:/C:/apps/glassfish/domains/domain1/applications/j2ee-modules/firebird-web/WEB-INF/lib
/firebird.jar-firebird.connection|_ThreadID=14;_ThreadName=Thread-24;|Connected: jdbc:firebirdsql:java://localhost/glassfish-jpa
User: sysdba
Database: Firebird 2.0*WI-V2.0.0.12748 Firebird 2.0/tcp (dev)/P10 Version: WI-V2.0.0.12748 Firebird 2.0*WI-V2.0.0.12748 Firebird 2.0/tcp (dev)/P10
Driver: Jaybird JCA/JDBC driver Version: 2.1|#]
[#|2007-02-26T08:18:15.562+0100|CONFIG|sun-appserver9.1|oracle.toplink.essentials.session.file:/C:/apps/glassfish/domains/domain1/applications/j2ee-modules/firebird-web/WEB-INF/lib
/firebird.jar-firebird.connection|_ThreadID=14;_ThreadName=Thread-24;|connecting(DatabaseLogin(
platform=>DatabasePlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))|#]
[#|2007-02-26T08:18:15.562+0100|CONFIG|sun-appserver9.1|oracle.toplink.essentials.session.file:/C:/apps/glassfish/domains/domain1/applications/j2ee-modules/firebird-web/WEB-INF/lib
/firebird.jar-firebird.connection|_ThreadID=14;_ThreadName=Thread-24;|Connected: jdbc:firebirdsql:java://localhost/glassfish-jpa
User: sysdba
Database: Firebird 2.0*WI-V2.0.0.12748 Firebird 2.0/tcp (dev)/P10 Version: WI-V2.0.0.12748 Firebird 2.0*WI-V2.0.0.12748 Firebird 2.0/tcp (dev)/P10
Driver: Jaybird JCA/JDBC driver Version: 2.1|#]
, czyli działa! A zatem na dzisiaj koniec.
Problemy
Najczęstszy to brak pamięci, który pojawia się pracując z plikami XML w tak "rozszerzonym" Eclipse IDE. Być może zwiększenie pamięci naprawiłoby problem, ale nie chciało mi się szukać.
I drugi, równie bolesny, to utrata stanu GlassFish przez wtyczkę GlassFish Eclipse plugin, kiedy w trakcie zatrzymywania serwera próbowałem uruchomić go ponownie. Rozwiązanie okazało się wymagające monitorowania stanu GF - podgląd dziennika zdarzeń GF ($GLASSFISH_HOME/domains/domain1/logs/server.log). Możemy uruchomić GF dopiero po pojawieniu się następujących wpisów w dzienniku zdarzeń:
[#|2007-02-26T08:19:39.796+0100|INFO|sun-appserver9.1|javax.enterprise.system.core|_ThreadID=15;_ThreadName=RMI TCP Connection(6)-127.0.0.1;|CORE5052: Application shutdown complete
.|#]
[#|2007-02-26T08:19:40.109+0100|INFO|sun-appserver9.1|javax.enterprise.system.tools.deployment|_ThreadID=15;_ThreadName=RMI TCP Connection(6)-127.0.0.1;|[AutoDeploy] Disabling Auto
Deployment service.|#]
Wcześniejsza próba uruchomienia będzie objawiała się tym, że Eclipse będzie raportował o próbie uruchomienia GF (Status=Starting...)
mimo, że tak na prawdę nie kontroluje GF i komunikat będzie trwał i trwał. Jedynym wyjściem wydaje się być zatrzymanie serwera z poziomu Eclipse IDE (tj. wciśnięcie ikony w postaci czerwonego kwadratu) i ponowne jego uruchomienie.
Czasami podczas uaktualniania aplikacji (ponownej jej instalacji na serwerze) pojawiał się błąd:
Buildfile: C:\.eclipse\jpa\.metadata\.plugins\org.eclipse.jst.server.generic.core\serverdef\sunappsrv-ant.xml
deploy.j2ee.web:
[echo] C:/.eclipse/jpa/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/firebird-web
[jar] Building jar: C:\.eclipse\jpa\.metadata\.plugins\org.eclipse.jst.server.generic.core\serverdef\firebird-web.war
tools:
deploy:
[exec] Command deploy executed successfully with following warning messages: WARNING:
[exec] The following file(s) were left(locked) from the previous deployment; they were not updated during this deployment and could interfere with the application:
[exec] C:\apps\glassfish\domains\domain1\applications\j2ee-modules\firebird-web\WEB-INF\lib\firebird.jar
deploy-url-message:
[echo] Application Deployed at: http://127.0.0.1:8080/firebird-web
BUILD SUCCESSFUL
Total time: 4 seconds
co obchodzimy przez zatrzymanie GF i ponowne uruchomienie.
Skłaniam się ku stwierdzeniu, że najmniej uciążliwym sposobem uaktualnienia aplikacji na serwerze GlassFish z użyciem wtyczki GlassFish Eclipse plugin jest po prostu zatrzymywanie serwera i jej instalacja za pomocą menu kontekstowego Publish (co automatycznie uruchomi również serwer, jeśli zatrzymany).
Brak komentarzy:
Prześlij komentarz