Skip to content

4. Örnek uygulama – 02: rdvmedecins-jsf2-spring

Şimdi önceki uygulamayı bir Spring / Tomcat ortamına taşımayı planlıyoruz:

Bu aslında bir taşıma işlemidir. Önceki uygulamadan yola çıkarak onu yeni ortama uyarlayacağız. Yalnızca değişiklikler üzerinde duracağız. Bu değişiklikler üç gruba ayrılır:

  • sunucu artık Glassfish değil, EJB konteyneri olmayan hafif bir sunucu olan Tomcat’tir,
  • EJB'in yerine, EJB ve [http://www.springsource.com/]'in başlıca rakibi olan Spring kullanılacak,
  • JPA uygulaması için EclipseLink yerine Hibernate kullanılacak.

Eski proje ile yeni proje arasında sık sık kopyala/yapıştır işlemi yapacağımız için, önceki projeleri NetBeans'te açık tutuyoruz:

  

Spring çerçevesini kullanmak için gerekli bazı bilgiler [ref7]'te bulunabilir (bkz. sayfa 166).

4.1. [DAO] ve [JPA] katmanları

4.1.1. NetBeans projesi

[Java Application] türünde bir Maven projesi oluşturuyoruz:

  • [1]'te, oluşturulan proje,
  • [2]'te, [Source Packages] ve [Test Packages] paketleri ile [junit-3.8.1] bağımlılığı kaldırılmış hali.

Maven projelerinde en zor olan şey, doğru bağımlılıkları bulmaktır. Bu Spring / JPA / Hibernate projesi için bunlar şunlardır:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>istia.st</groupId>
  <artifactId>mv-rdvmedecins-spring-dao-jpa</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>mv-rdvmedecins-spring-dao-jpa</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

    <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>4.1.2</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.hibernate.java-persistence</groupId>
      <artifactId>jpa-api</artifactId>
      <version>2.0.Beta-20090815</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>commons-pool</groupId>
      <artifactId>commons-pool</artifactId>
      <version>1.6</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>3.1.1.RELEASE</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>3.1.1.RELEASE</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.1.1.RELEASE</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>3.1.1.RELEASE</version>
      <type>jar</type>
    </dependency>
  </dependencies>

</project>
  • 18-29. satırlar: Hibernate için,
  • 30-34. satırlar: MySQL'in JDBC sürücüsü için,
  • 35-41. satırlar: JUnit testi için,
  • satır 42-51: Apache Commons DBCP bağlantı havuzu için. Bir bağlantı havuzu, açık bağlantıların bir havuzudur. Uygulama bir bağlantıya ihtiyaç duyduğunda, bunu havuzdan ister. Artık ihtiyaç duymadığında ise geri verir. Bağlantılar, uygulamanın başlatılmasıyla açılır ve uygulamanın ömrü boyunca açık kalır. Bu, bağlantıların tekrar tekrar açılıp kapatılmasından kaynaklanan maliyeti önler. Bu tür bir havuz Glassfish’te de mevcuttu, ancak kullanımı bizim için şeffaf bir şekilde gerçekleşiyordu. Burada da durum aynı olacak, ancak bunu kurup yapılandırmamız gerekiyor,
  • 52-75. satırlar: Spring için.

Bu bağımlılıkları ekleyip projeyi derleyelim:

  • [1] olarak, projeyi derlediğimizde Maven bağımlılıkları indirmeye zorlanacak,
  • [2]'te, bunlar daha sonra [Dependencies] dalında görünür. Hibernate ve Spring çerçevelerinin kendilerinin de çok sayıda bağımlılığı olması nedeniyle bunların sayısı oldukça fazladır. Yine burada, Maven sayesinde bunlarla ilgilenmemize gerek yoktur. Otomatik olarak indirilirler.

Artık bağımlılıklara sahip olduğumuza göre, [dao] katmanındaki EJB projesinin kodunu, [dao] katmanındaki Spring projesine yapıştırıyoruz:

  • [1]'e, kaynak projeye kopyalayalım,
  • [2] dosyasını hedef projeye yapıştırın,
  • [3]'te sonuç.

Kopyalama işlemi tamamlandıktan sonra hataları düzeltmek gerekir.

4.1.2. [exceptions] paketi

[RdvMedecinsExceptions] [1] sınıfında, artık mevcut olmayan [javax] paketinin 4. satırı nedeniyle hatalar bulunmaktadır. Bu, EJB'e özgü bir pakettir. 6. satırdaki hata, 4. satırdaki hatadan kaynaklanmaktadır. Bu iki satır silinir. Böylece [2] hataları da ortadan kalkar.

4.1.3. [jpa] paketi

  • [1]'te, [5] satırındaki doğrulama paketi eksik olduğu için [Creneau] sınıfında hata oluşuyor. Bu paketi projenin bağımlılıklarına ekleyebilirdik. Ancak testler sırasında Hibernate, bu paket nedeniyle bir istisna oluşturuyor. Uygulamamız için zorunlu olmadığı için onu kaldırdık. Sınıfı düzeltmek için, hatalı olan tüm [2] satırlarını silmek yeterlidir. Hatalı olan tüm sınıflar için bunu yapıyoruz.

4.1.4. [dao] paketi

Şu anda şu noktadayız:

  • [1]'te, düzeltilmiş iki paket,
  • [2]'e, [dao] paketi. Artık EJB olmadığı için, EJB'in uzak ve yerel arayüz kavramı da ortadan kalkmıştır. Bunları [3]'ten siliyoruz.
  • [1]'te, [DaoJpa] sınıfındaki hataların iki nedeni vardır:
  • EJB ile ilişkili bir paketin içe aktarılması (6-8. satırlar);
  • az önce sildiğimiz yerel ve uzak arayüzlerin kullanılması.

Hatalı satırları siliyoruz ve yerel ve uzak [2] arayüzleri yerine [IDao] arayüzünü kullanıyoruz.

EJB projesinde, [DaoJpa] sınıfı bir singleton'dı ve yöntemleri bir işlem içinde yürütülüyordu. Göreceğimiz üzere, [DaoJpa] sınıfı Spring tarafından yönetilen bir bean olacaktır. Varsayılan olarak tüm Spring bean'leri singleton'dur. İlk özellik bu kadar. İkinci özellik ise Spring'in @Transactional anotasyonu ile elde edilir: [3]:

Bu işlem tamamlandıktan sonra, projede artık hata görünmüyor [4].

4.1.5. [JPA] katmanının yapılandırılması

EJB projesinde, [JPA] katmanını [persistence.xml] dosyasıyla yapılandırmıştık. Burada bir [JPA] katmanımız var ve bu nedenle bu dosyayı oluşturmamız gerekiyor. EJB projesinde bu dosyayı Glassfish ile oluşturmuştuk. Burada ise dosyayı manuel olarak oluşturuyoruz. Bunun temel nedeni, [persistence.xml] dosyasının yapılandırmasının bir kısmının Spring'in kendi yapılandırma dosyasına taşınmasıdır.

[persistence.xml] dosyasını oluşturuyoruz:

dosyasını şu içerikle oluşturuyoruz:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.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_2_0.xsd">
  <persistence-unit name="spring-dao-jpa-hibernate-mysqlPU" transaction-type="RESOURCE_LOCAL">
    <class>rdvmedecins.jpa.Client</class>
    <class>rdvmedecins.jpa.Creneau</class>
    <class>rdvmedecins.jpa.Medecin</class>
    <class>rdvmedecins.jpa.Rv</class>
  </persistence-unit>
</persistence>
  • 3. satır: kalıcılık birimine bir ad veriyoruz,
  • 3. satır: işlem türü RESOURCE_LOCAL'tir. EJB projesinde, işlemlerin EJB konteyneri tarafından yönetildiğini belirtmek için JTA kullanılmıştı. RESOURCE_LOCAL değeri, uygulamanın işlemlerini kendi başına yönettiğini gösterir. Burada bu, Spring aracılığıyla gerçekleştirilecektir,
  • 4-7. satırlar: dört varlığın tam adları JPA. Bu isteğe bağlıdır çünkü Hibernate bunları projenin ClassPath'inde otomatik olarak arar.

Hepsi bu kadar. JPA sağlayıcısının adı, özellikleri ve JDBC veri kaynağının özellikleri artık Spring yapılandırma dosyasında yer almaktadır.

4.1.6. Spring yapılandırma dosyası

[DaoJpa] sınıfının Spring tarafından yönetilen bir bean olduğunu belirtmiştik. Bu, bir yapılandırma dosyası aracılığıyla gerçekleştirilir. Bu dosya, veritabanına erişim yapılandırmasının yanı sıra işlem yönetimini de içerecektir. Dosya, projenin ClassPath klasöründe bulunmalıdır. Bunu [Other sources] dalına yerleştiriyoruz:

[spring-config-dao.xml] dosyası şu şekildedir:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

  <!-- uygulama katmanları -->
  <bean id="dao" class="    " />
  
  <!-- EntityManagerFactory -->
  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
        <!--
        <property name="showSql" value="true" />
        <property name="generateDdl" value="true" />
        -->
      </bean>
    </property>
  </bean>

  <!-- veri kaynağı DBCP -->
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/dbrdvmedecins2" />
    <property name="username" value="root" />
    <property name="password" value="" />
  </bean>

  <!-- işlem yöneticisi -->
  <tx:annotation-driven transaction-manager="txManager" />
  <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
  </bean>

  <!-- istisna çevirisi -->
  <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

  <!-- kalıcılık -->
  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

</beans>

Bu, Spring 2.x ile uyumlu bir dosyadır. 3.x sürümlerinin yeni özelliklerini kullanmaya çalışmadık.

  • 2-4. satırlar: yapılandırma dosyasının kök <beans> etiketi. Bu etiketin çeşitli öznitelikleri hakkında yorum yapmayacağız. Bu özniteliklerden birinde hata yapılması, bazen anlaşılması zor hatalara yol açtığı için, kopyala/yapıştır işlemini dikkatli bir şekilde gerçekleştirmeliyiz,
  • 7. satır: "dao" bean'i, [rdvmedecins.dao.DaoJpa] sınıfının bir örneğine yapılan bir referanstır. Tek bir örnek oluşturulacak (singleton) ve uygulamanın [dao] katmanını uygulayacaktır,
  • 24-29. satırlar: Bir veri kaynağı tanımlanmıştır. Bu kaynak, daha önce bahsettiğimiz "bağlantı havuzu" hizmetini sağlar. Burada, Apache Commons projesine ait DBCP ve [http://jakarta.apache.org/commons/dbcp/] sınıflarından oluşan [DBCP] kullanılmaktadır,
  • 25-28. satırlar: Hedef veritabanıyla bağlantı kurmak için veri kaynağının, kullanılan JDBC sürücüsünü (25. satır), veritabanının URL'sini (26. satır), bağlantı kullanıcısını ve şifresini (satır 27-28),
  • satır 10-21: JPA katmanını yapılandırır,
  • 10. satır: kalıcılık bağlamlarını yönetmek için [EntityManager] türünde nesneler oluşturabilen [EntityManagerFactory] türünde bir bean tanımlar. Oluşturulan [LocalContainerEntityManagerFactoryBean] sınıfı Spring tarafından sağlanır. Bu sınıfın oluşturulabilmesi için 11-20. satırlarda tanımlanan bir dizi parametreye ihtiyaç duyar,
  • 11. satır: SGBD'e bağlantı kurmak için kullanılacak veri kaynağı. Bu, 24-29. satırlarda tanımlanan [DBCP] kaynağıdır,
  • 12-20. satırlar: Kullanılacak JPA uygulaması,
  • 13. satır: Hibernate'i kullanılacak JPA uygulaması olarak tanımlar,
  • 14. satır: Hibernate'in SGBD hedefi ile birlikte kullanması gereken SQL lehçesi, burada MySQL5,
  • 16. satır (yorum olarak): Hibernate tarafından yürütülen SQL komutlarının konsola kaydedilmesini ister,
  • 17. satır (yorum olarak): uygulamanın başlatılması sırasında veritabanının oluşturulmasını (drop ve create) talep eder,
  • 32. satır: işlemlerin Java anotasyonlarıyla yönetildiğini belirtir (bunlar spring-config.xml içinde de tanımlanabilirdi). Özellikle [DaoJpa] sınıfında bulunan @Transactional anotasyonu,
  • 33-35. satırlar: kullanılacak işlem yöneticisini tanımlar,
  • 33. satır: İşlem yöneticisi, Spring tarafından sağlanan bir sınıftır,
  • 34. satır: Spring işlem yöneticisi, JPA katmanını yöneten EntityManagerFactory'i bilmelidir. Bu, 10-21. satırlarda tanımlanan sınıftır,
  • satır 41: Spring kalıcılık anotasyonlarını yöneten sınıfı tanımlar,
  • 38. satır: Özellikle @Repository anotasyonunu yöneten Spring sınıfını tanımlar; bu anotasyon, bu şekilde anotasyonlanmış bir sınıfın, JDBC sürücüsündeki yerel istisnaların SGBD türünden [DataAccessException] türündeki genel Spring istisnalarına dönüştürülmesine uygun hale getirir. Bu dönüştürme, JDBC yerel istisnasını, çeşitli alt sınıfları olan [DataAccessException] türüne kapsüller:

Image

Bu dönüşüm, istemci programının hedef SGBD ne olursa olsun istisnaları genel bir şekilde yönetmesini sağlar. Java kodumuzda @Repository anotasyonunu kullanmadık. Dolayısıyla 38. satır gereksizdir. Yalnızca bilgilendirme amacıyla bu satırı bıraktık.

Spring yapılandırma dosyası ile işimiz bitti. Bu dosya, Spring belgelerinden alınmıştır. Çeşitli durumlara uyarlanması genellikle iki değişiklikle özetlenebilir:

  • hedef veritabanı değişikliği: 24-29. satırlar,
  • JPA uygulamasının değiştirilmesi: 12-20. satırlar.

Kod çalıştırıldığında, yapılandırma dosyasındaki tüm bean'ler örneklenecektir. Bunun nasıl yapıldığını göreceğiz.

4.1.7. JUnit test sınıfı

EJB projesinin [DAO] katmanını JUnit testi ile test etmiştik. Aynı işlemi Spring projesinin [DAO] katmanı için de yapıyoruz:

  • [1] ve [2]'te, JUnit testini iki proje arasında kopyalayıp yapıştırdığımızda,
  • [3]'te, içe aktarılan test yeni ortamında hatalar veriyor.

Bildirilen [1] hatası, artık mevcut olmayan EJB'in uzak arayüzüne aittir. Ayrıca, 19. satırdaki [dao] alanının başlatma kodu, EJB'e (25-28. satırlar) özgü bir JNDI çağrısıydı. 19. satırdaki [dao] alanını oluşturmak için Spring yapılandırma dosyasını kullanmamız gerekiyor. Bu, şu şekilde yapılır:

  • 21. satır: arayüzün türü [IDao] olarak değiştirilmiştir,
  • 28. satır: [spring-config-dao.xml] dosyasında bildirilen tüm bean'leri örneklendirir, özellikle de şunu:

  <bean id="dao" class="rdvmedecins.dao.DaoJpa" />
  • 29. satır, 28. satırdaki Spring bağlamından id="dao" olan bean'e bir referans ister. Böylece, Spring'in oluşturduğu [DaoJpa] (yukarıda class) singleton'una bir referans elde edilir.

28-29. satırlar aşağıdaki blokları oluşturur (pembe noktalı çizgiler):

JUnit istemcisinin testleri çalıştırıldığında, [DAO] katmanı örneklenmiştir. Dolayısıyla bu katmanın yöntemlerini test edebiliriz. EJB ve [DAO] testlerinin Glassfish sunucusunu gerektirmesinin aksine, bu testi yapmak için sunucuya gerek olmadığını belirtelim. Burada her şey aynı JVM içinde yürütülür.

Artık JUnit testini çalıştırabiliriz. Bunun için MySQL sunucusunun çalışır durumda olması gerekir. Sonuçlar şu şekildedir:

JUnit testi başarılı oldu. EJB testinde olduğu gibi test günlüklerini inceleyelim:

mai 24, 2012 5:10:29 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
Infos: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@67291453: startup date [Thu May 24 17:10:29 CEST 2012]; root of context hierarchy
mai 24, 2012 5:10:29 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
...
mai 24, 2012 5:10:30 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
mai 24, 2012 5:10:30 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.2}
mai 24, 2012 5:10:30 PM org.hibernate.cfg.Environment <clinit>
...
Infos: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6affe94b: defining beans [dao,entityManagerFactory,dataSource,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,txManager,org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0,org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor#0]; fabrika hiyerarşisinin kökü
Liste des clients :
Client[1,Mr,Jules,MARTIN]
Client[2,Mme,Christine,GERMAN]
Client[3,Mr,Jules,JACQUARD]
Client[4,Melle,Brigitte,BISTROU]
Liste des médecins :
Médecin[1,Mme,Marie,PELISSIER]
Médecin[2,Mr,Jacques,BROMARD]
Médecin[3,Mr,Philippe,JANDOT]
Médecin[4,Melle,Justine,JACQUEMOT]
Liste des créneaux du médecin Médecin[1,Mme,Marie,PELISSIER]
Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [2, 1, 8:20, 8:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [4, 1, 9:0, 9:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [5, 1, 9:20, 9:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [6, 1, 9:40, 10:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [7, 1, 10:0, 10:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [8, 1, 10:20, 10:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [9, 1, 10:40, 11:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [10, 1, 11:0, 11:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [11, 1, 11:20, 11:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [12, 1, 11:40, 12:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [13, 1, 14:0, 14:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [14, 1, 14:20, 14:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [15, 1, 14:40, 15:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [16, 1, 15:0, 15:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [17, 1, 15:20, 15:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [18, 1, 15:40, 16:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [19, 1, 16:0, 16:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [20, 1, 16:20, 16:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [21, 1, 16:40, 17:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [22, 1, 17:0, 17:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [23, 1, 17:20, 17:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [24, 1, 17:40, 18:0,Médecin[1,Mme,Marie,PELISSIER]]
Liste des créneaux du médecin Médecin[1,Mme,Marie,PELISSIER], le [Thu May 24 17:10:30 CEST 2012]
Rv[211, Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]], Client[4,Melle,Brigitte,BISTROU]]
Ajout d'un Rv le [Thu May 24 17:10:30 CEST 2012] dans le créneau Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]] pour le client Client[1,Mr,Jules,MARTIN]
avant persist : Rv[null, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
après persist : Rv[216, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
Rv ajouté
mai 24, 2012 5:10:31 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
Liste des Rv du médecin Médecin[1,Mme,Marie,PELISSIER], le [Thu May 24 17:10:30 CEST 2012]
WARN: SQL Error: 1062, SQLState: 23000
Rv[211, Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]], Client[4,Melle,Brigitte,BISTROU]]
Rv[216, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
mai 24, 2012 5:10:31 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
Ajout d'un Rv le [Thu May 24 17:10:30 CEST 2012] dans le créneau Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]] pour le client Client[1,Mr,Jules,MARTIN]
ERROR: Duplicate entry '2012-05-24-3' for key 'UNQ1_RV'
avant persist : Rv[null, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Duplicate entry '2012-05-24-3' for key 'UNQ1_RV'
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Duplicate entry '2012-05-24-3' for key 'UNQ1_RV'
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Duplicate entry '2012-05-24-3' for key 'UNQ1_RV'
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Duplicate entry '2012-05-24-3' for key 'UNQ1_RV'
Liste des Rv du médecin Médecin[1,Mme,Marie,PELISSIER], le [Thu May 24 17:10:30 CEST 2012]
Rv[211, Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]], Client[4,Melle,Brigitte,BISTROU]]
Rv[216, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
Suppression du Rv ajouté
Rv supprimé
Liste des Rv du médecin Médecin[1,Mme,Marie,PELISSIER], le [Thu May 24 17:10:30 CEST 2012]
Rv[211, Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]], Client[4,Melle,Brigitte,BISTROU]]
  • 1-4. satırlar: Spring günlükleri,
  • 5-10. satırlar: Hibernate günlükleri,
  • 11. satır: Spring, oluşturduğu tüm bean'leri bildirir. Burada ilk olarak [dao] bean'i yer alır,
  • 12. satır ve sonrası: JUnit testinin günlükleri,
  • 60-65. satırlar: Veritabanında zaten mevcut olan bir randevunun eklenmesiyle ortaya çıkan istisna açıkça görülüyor. Hatırlatmak gerekirse, EJB'te serileştirme sorunu nedeniyle bu istisna ortaya çıkmamıştı.

[dao] katmanı çalışır durumdadır. Şimdi [métier] katmanını oluşturuyoruz.

4.2. [métier] katmanı

[DAO] katmanında yaptığımızın aynısını yapıyoruz; EJB projesinden Spring projesine kopyala/yapıştır işlemi gerçekleştiriyoruz.

4.2.1. NetBeans projesi

[Java Application] türünde yeni bir Maven projesi oluşturuyoruz; bu projede, [1]'te tutmak istemediğimiz her şey temizlenmiştir:

4.2.2. Projenin bağımlılıkları

Mimari içinde:

[métier] katmanı, [dao] katmanına dayanmaktadır. Bu nedenle, önceki projeye bir bağımlılık ekliyoruz:

  • [1] ve [2]'te, [dao] katman projesine bir bağımlılık ekliyoruz,
  • [3]'te ise bu bağımlılık, [dao] katman projesine ait başka bağımlılıkları da beraberinde getirdi.
  • [1] ve [2] projelerine, EJB projesinin Java kaynak kodları Spring projesine kopyalanır,
  • [3]'e kopyalandığında, içe aktarılan kaynak kodlar yeni ortamlarında hatalar veriyor.

İlk olarak, artık mevcut olmayan [métier] katmanındaki uzak ve yerel arayüzleri [4]'ten siliyoruz:

  • [5]'te, [Metier] sınıfındaki hataların birkaç nedeni vardır:
    • artık mevcut olmayan [javax.ejb] paketinin kullanılması;
    • artık mevcut olmayan [IDaoLocal] arayüzünün kullanılması;
    • artık mevcut olmayan [IMetierRemote] ve [IMetierLocal] arayüzlerinin kullanılması.

Biz

  • [javax.ejb] paketiyle ilgili tüm hatalı satırları siliyoruz,
  • [IDaoLocal] arayüzünü [IDao] arayüzüyle değiştiriyoruz,
  • [IMetierRemote] ve [IMetierLocal] arayüzlerini [IMetier] arayüzüyle değiştiriyoruz.
  • [6] ile değiştirildi; böylece sınıf düzeltildi,
  • [7]'te artık hata yok.

EJB'e yapılan referansları sildik, ancak şimdi bunların özelliklerini bulmamız gerekiyor:

 
  • 22. satır: Bir singleton vardı. Bu özellik, sınıfı Spring tarafından yönetilen bir bean haline getirerek elde edilecektir,
  • 23. satır: Her yöntem bir işlem içinde yürütülüyordu. Bu, Spring’in @Transactional anotasyonu ile sağlanacaktır,
  • 27-28. satırlar: [DAO] katmanındaki referans, EJB konteynerinin enjeksiyonu yoluyla elde ediliyordu. Spring enjeksiyonunu kullanacağız.

Böylece, Spring projesindeki [Metier] sınıfının kodu şu şekilde değişir:

Java kodu bu kadar. Geri kalanı Spring yapılandırma dosyasında gerçekleşir.

4.2.3. Spring yapılandırma dosyası

[DAO] katman projesindeki Spring yapılandırma dosyasını, [métier] katman projesine kopyalıyoruz. Öncelikle, [métier] katman projesinde [Other Resources] dalı yoksa bunu oluşturuyoruz:

  • [1]'te, [Files] sekmesinde, [main] klasörüne bir alt klasör oluşturuyoruz;
  • [2]'te, adı [resources] olmalıdır,
  • [3]'te, [Projects] sekmesinde, [Other Sources] dalı oluşturulmuştur.

Şimdi Spring yapılandırma dosyasını kopyalayıp yapıştırabiliriz:

  • [1]'te, [DAO] projesindeki dosyayı [métier] ve [2] projelerine,
  • kopyalanan dosya olan [3]'e.

Kopyalanan yapılandırma dosyası, [DAO] katmanını yapılandırır. [métier] katmanını yapılandırmak için bu dosyaya bir bean ekliyoruz:

1
2
3
4
5
   <!-- uygulama katmanları -->
  <bean id="dao" class="rdvmedecins.dao.DaoJpa" />
  <bean id="metier" class="rdvmedecins.metier.service.Metier">
    <property name="dao" ref="dao"/>
</bean>
  • 2. satır: [DAO] katmanının bean'i,
  • 3-5. satırlar: [métier] katmanının bean'i,
  • 3. satır: bean'in adı métier'tir (id özniteliği) ve [rdvmedecins.metier.service.Metier] sınıfının bir örneğidir (class özniteliği). Bu bean, diğerleri gibi uygulamanın başlatılması sırasında örneklenecektir.

[rdvmedecins.metier.service.Metier] bean'inin kodunu hatırlayalım:


package rdvmedecins.metier.service;

...

public class Metier implements IMetier, Serializable {

  // DAO katmanı
  private IDao dao;

  public Metier() {
}
  • 8. satır: [dao] alanı, métier bean'iyle aynı anda Spring tarafından örneklenecektir. Spring yapılandırma dosyasındaki bu bean'in tanımına geri dönelim:
1
2
3
4
5
   <!-- uygulama katmanları -->
  <bean id="dao" class="rdvmedecins.dao.DaoJpa" />
  <bean id="metier" class="rdvmedecins.metier.service.Metier">
    <property name="dao" ref="dao"/>
</bean>
  • 4. satır: <property> etiketi, örneklenen bean'in alanlarını başlatmak için kullanılır. Alanın adı, name özniteliği ile belirtilir. Dolayısıyla, [rdvmedecins.metier.service.Metier] sınıfının dao alanı örneklenecektir. Bu, mevcut olması gereken bir setDao yöntemi aracılığıyla gerçekleştirilecektir. Bu alana atanacak değer, ref özniteliğinin değeridir. Bu değer, burada 2. satırdaki dao bean'inin referansıdır.

Daha basit bir ifadeyle, kodda:


package rdvmedecins.metier.service;

...

public class Metier implements IMetier, Serializable {

  // DAO katmanı
  private IDao dao;

  public Metier() {
}
  1. satırdaki dao alanı, Spring tarafından [dao] katmanına bir referansla başlatılacaktır. İşte istediğimiz de buydu. dao alanı, Spring tarafından eklememiz gereken bir setter aracılığıyla başlatılacaktır:

  // setter

  public void setDao(IDao dao) {
    this.dao = dao;
}

Değişiklikleri yansıtmak için Spring yapılandırma dosyasının adını değiştiriyoruz:

Artık test yapmaya hazırız. EJB ve [Metier]'i test etmek için kullandığımız konsol testini tekrar uyguluyoruz.

4.2.4. [métier] katmanının testi

Test, aşağıdaki mimari ile gerçekleştirilecektir:

EJB projesindeki konsol testini Spring projesine kopyalıyoruz:

  • [1] ve [2] projelerine, iki proje arasında kopyala/yapıştır işlemi yapıldığında,
  • [3]'te, içe aktarılan kodta hatalar görülüyor.
 

İçe aktarılan kodda iki tür hata var:

  • 13. satır: [IMetierRemote] arayüzü, [IMetier] arayüzüyle değiştirilmiştir,
  • 24-27. satırlar: [métier] katmanının örneklenmesi artık JNDI çağrısıyla değil, Spring yapılandırma dosyasındaki bean'lerin örneklenmesiyle gerçekleştirilmektedir.

Bu iki noktayı düzeltiyoruz:

  • 22. satır: [spring-config-metier-dao.xml] dosyası kullanılıyor. Bu dosyanın tüm bean'leri bu şekilde oluşturuluyor. Bunlar arasında şunlar bulunuyor:

  <!-- uygulama katmanları -->
  <bean id="dao" class="rdvmedecins.dao.DaoJpa" />
  <bean id="metier" class="rdvmedecins.metier.service.Metier">
    <property name="dao" ref="dao"/>
</bean>

Bu iki bean, test mimarisindeki [DAO] ve [métier] katmanlarını temsil eder:

Bu işlem tamamlandıktan sonra test gerçekleştirilebilir:

  

Test günlükleri şu şekildedir:

mai 25, 2012 9:45:07 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
Infos: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@22a92801: startup date [Fri May 25 09:45:07 CEST 2012]; root of context hierarchy
mai 25, 2012 9:45:07 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
....
Infos: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@38a0a058: defining beans [dao,metier,entityManagerFactory,dataSource,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,txManager,org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0,org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor#0]; fabrika hiyerarşisinin kökü
Liste des clients :
Client[1,Mr,Jules,MARTIN]
Client[2,Mme,Christine,GERMAN]
Client[3,Mr,Jules,JACQUARD]
Client[4,Melle,Brigitte,BISTROU]
Liste des médecins :
Médecin[1,Mme,Marie,PELISSIER]
Médecin[2,Mr,Jacques,BROMARD]
Médecin[3,Mr,Philippe,JANDOT]
Médecin[4,Melle,Justine,JACQUEMOT]
Liste des créneaux du médecin Médecin[1,Mme,Marie,PELISSIER]
Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [2, 1, 8:20, 8:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [4, 1, 9:0, 9:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [5, 1, 9:20, 9:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [6, 1, 9:40, 10:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [7, 1, 10:0, 10:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [8, 1, 10:20, 10:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [9, 1, 10:40, 11:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [10, 1, 11:0, 11:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [11, 1, 11:20, 11:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [12, 1, 11:40, 12:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [13, 1, 14:0, 14:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [14, 1, 14:20, 14:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [15, 1, 14:40, 15:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [16, 1, 15:0, 15:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [17, 1, 15:20, 15:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [18, 1, 15:40, 16:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [19, 1, 16:0, 16:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [20, 1, 16:20, 16:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [21, 1, 16:40, 17:0,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [22, 1, 17:0, 17:20,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [23, 1, 17:20, 17:40,Médecin[1,Mme,Marie,PELISSIER]]
Creneau [24, 1, 17:40, 18:0,Médecin[1,Mme,Marie,PELISSIER]]
Liste des rendez-vous du médecin Médecin[1,Mme,Marie,PELISSIER], le [Fri May 25 09:45:07 CEST 2012]
Agenda[Médecin[1,Mme,Marie,PELISSIER],25/05/2012, [Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [2, 1, 8:20, 8:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [4, 1, 9:0, 9:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [5, 1, 9:20, 9:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [6, 1, 9:40, 10:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [7, 1, 10:0, 10:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [8, 1, 10:20, 10:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [9, 1, 10:40, 11:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [10, 1, 11:0, 11:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [11, 1, 11:20, 11:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [12, 1, 11:40, 12:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [13, 1, 14:0, 14:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [14, 1, 14:20, 14:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [15, 1, 14:40, 15:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [16, 1, 15:0, 15:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [17, 1, 15:20, 15:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [18, 1, 15:40, 16:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [19, 1, 16:0, 16:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [20, 1, 16:20, 16:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [21, 1, 16:40, 17:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [22, 1, 17:0, 17:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [23, 1, 17:20, 17:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [24, 1, 17:40, 18:0,Médecin[1,Mme,Marie,PELISSIER]] null]]
Ajout d'un Rv le [Fri May 25 09:45:07 CEST 2012] dans le créneau Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]] pour le client Client[1,Mr,Jules,MARTIN]
avant persist : Rv[null, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
après persist : Rv[220, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
Rv ajouté
Liste des Rv du médecin Médecin[1,Mme,Marie,PELISSIER], le [Fri May 25 09:45:07 CEST 2012]
Rv[220, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]
Agenda[Médecin[1,Mme,Marie,PELISSIER],25/05/2012, [Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [2, 1, 8:20, 8:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]] Rv[220, Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]], Client[1,Mr,Jules,MARTIN]]] [Creneau [4, 1, 9:0, 9:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [5, 1, 9:20, 9:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [6, 1, 9:40, 10:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [7, 1, 10:0, 10:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [8, 1, 10:20, 10:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [9, 1, 10:40, 11:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [10, 1, 11:0, 11:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [11, 1, 11:20, 11:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [12, 1, 11:40, 12:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [13, 1, 14:0, 14:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [14, 1, 14:20, 14:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [15, 1, 14:40, 15:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [16, 1, 15:0, 15:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [17, 1, 15:20, 15:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [18, 1, 15:40, 16:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [19, 1, 16:0, 16:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [20, 1, 16:20, 16:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [21, 1, 16:40, 17:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [22, 1, 17:0, 17:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [23, 1, 17:20, 17:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [24, 1, 17:40, 18:0,Médecin[1,Mme,Marie,PELISSIER]] null]]
Suppression du Rv ajouté
Rv supprimé
Liste des Rv du médecin Médecin[1,Mme,Marie,PELISSIER], le [Fri May 25 09:45:07 CEST 2012]
Agenda[Médecin[1,Mme,Marie,PELISSIER],25/05/2012, [Creneau [1, 1, 8:0, 8:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [2, 1, 8:20, 8:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [3, 1, 8:40, 9:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [4, 1, 9:0, 9:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [5, 1, 9:20, 9:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [6, 1, 9:40, 10:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [7, 1, 10:0, 10:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [8, 1, 10:20, 10:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [9, 1, 10:40, 11:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [10, 1, 11:0, 11:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [11, 1, 11:20, 11:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [12, 1, 11:40, 12:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [13, 1, 14:0, 14:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [14, 1, 14:20, 14:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [15, 1, 14:40, 15:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [16, 1, 15:0, 15:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [17, 1, 15:20, 15:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [18, 1, 15:40, 16:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [19, 1, 16:0, 16:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [20, 1, 16:20, 16:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [21, 1, 16:40, 17:0,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [22, 1, 17:0, 17:20,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [23, 1, 17:20, 17:40,Médecin[1,Mme,Marie,PELISSIER]] null] [Creneau [24, 1, 17:40, 18:0,Médecin[1,Mme,Marie,PELISSIER]] null]]
  • 1-4. satırlar: Spring ve Hibernate günlükleri,
  • 5. satır: Spring tarafından örneklendirilen bean’ler. DAO ve iş mantığı bean’lerine dikkat edilmelidir,
  • 6-53. satırlar: Test günlükleri. Bunlar, EJB projesinin testinde elde edilen sonuçlarla uyumludur. Okuyucuyu bu testin açıklamalarına (3.5.3. paragraf) yönlendiriyoruz.

[métier] katmanını oluşturduk. Şimdi son katmana, yani [web] katmanına geçiyoruz.

4.3. [web] katmanı

[web] katmanını oluşturmak için, diğer iki katmanda olduğu gibi, EJB projesindeki [web] katmanından kopyala/yapıştır yöntemini kullanacağız.

4.3.1. NetBeans projesi

Öncelikle bir web projesi oluşturacağız:

  • [1] içinde yeni bir proje oluşturuyoruz,
  • [2]'te, [Web Application] türünde bir Maven projesi oluşturuyoruz,
  • [3]'te ona bir isim veriyoruz,
  • [4]'te, bu sefer EJB projesinde kullanılan Glassfish yerine Tomcat sunucusunu seçiyoruz,
  • [5]'te elde edilen proje,
  • [6], [index.jsp] ve [Source Packages] paketleri silindikten sonraki proje.

4.3.2. Projenin bağımlılıkları

Projenin mimarisine bir göz atalım:

[web] katmanı, [métier], [DAO] ve [JPA] katmanlarına ihtiyaç duyar. Bunlar, az önce derlediğimiz iki projenin parçasıdır. Dolayısıyla, bu projelerin her birine bir bağımlılık oluşur:

  • [1] projesine Spring / iş projesi bağımlılığını ekliyoruz,
  • [2]'te ise Spring / iş projesi eklenmiştir. Bu projenin kendisi de Spring / DAO / JPA projesine bağımlı olduğu için, bu proje otomatik olarak [3] projesinin bağımlılıkları arasına eklenmiştir.

Uygulamamızın yapısına geri dönelim:

Web katmanı, JSF katmanıdır. Dolayısıyla Java Server Faces kütüphanelerine ihtiyacımız var. Tomcat sunucusunda bu kütüphaneler bulunmuyor. Bu nedenle bağımlılık, Glassfish sunucusunda olduğu gibi [provided] kapsamında değil, kapsam belirtilmediğinde varsayılan olan [compile] kapsamında olacaktır.

Bu bağımlılıkları doğrudan [pom.xml] koduna ekliyoruz:


<dependencies>
    <dependency>
      <groupId>${project.groupId}</groupId>
      <artifactId>mv-rdvmedecins-spring-metier</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.1.7</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.7</version>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  • 7-16. satırlar [pom.xml] dosyasına eklenmiştir. Bunlar, JSF ile ilgili bağımlılıklardır. Bunlar, EJB / Glassfish projesinde kullanılan bağımlılıklardır. Bu bağımlılıkların <scope> etiketine sahip olmadığına dikkat edilmelidir. Dolayısıyla varsayılan olarak [compile] kapsamına sahiptirler. Bu nedenle JSF kütüphanesi, web projesinin [war] arşivine dahil edilecektir.

Bu bağımlılıkları [pom.xml] dosyasına ekledikten sonra, bunların indirilmesi için projeyi derliyoruz.

4.3.3. JSF / Glassfish projesinin JSF / Tomcat projesine taşınması

JSF / Glassfish projesindeki tüm kodları JSF / Tomcat projesine kopyalıyoruz:

  • [1, 2, 3]: eski projenin web sayfalarının yeni projeye kopyalanması,
  • [1, 2, 3]: Eski projenin Java kodlarının yeni projeye kopyalanması. Bazı hatalar var. Bu normaldir. Bunları düzelteceğiz,
  • [1]'te, NetBeans'in [Files] sekmesinde, [main] klasörüne [resources] adlı bir alt klasör oluşturulur,
  • bu işlem, [Projects] sekmesinde [Other Sources] ve [3] dallarını oluşturur,
  • [1, 2, 3]: Eski projenin mesaj dosyaları yeni projeye kopyalanır.

4.3.4. İçe aktarılan projedeki değişiklikler

İçe aktarılan Java kodunda hatalar olduğunu belirtmiştik. Bunları inceleyelim:

  • [1]'te yalnızca [Application] bean'i hatalıyken,
  • [2]'te ise hata, yalnızca artık mevcut olmayan [IMetierLocal] arayüzünden kaynaklanmaktadır. Burada, 20. satırın hata olarak işaretlenmemesi şaşırtıcı olabilir. @EJB açıklaması, EJB'e açıkça atıfta bulunur ve burada tanınır. Bunun nedeni, [javaee-web-api-6.0] ve [3] bağımlılıklarının varlığıdır. Java EE 6, uzaktan arayüzü olmayan EJB'lere dayanan bir web uygulamasını, EJB konteyneri bulunmayan sunucularda dağıtmaya olanak tanıyan bir mimariyi beraberinde getirmiştir. Sunucunun sadece [javaee-web-api-6.0] bağımlılığını sağlaması yeterlidir. Nitekim bu bağımlılığın kapsamının [provided] [3] olduğu görülmektedir.

Burada [javaee-web-api-6.0] bağımlılığını kullanmayacağız. [1]'i siliyoruz:

Bu, [2] hatalarını ortaya çıkarır. Öncelikle [Form] bean'iyle ilgili hatalardan başlayalım:

  • [1]'te, hatalı satırlar [javax] paketinin kaybolmasıyla ilgilidir. Hepsini siliyoruz: [2]. Hatalı satırlar, [Form] sınıfını oturum kapsamlı bir bean haline getiriyordu ([1] dosyasının 18-20. satırları). Ayrıca, [Application] bean'i 25. satırda enjekte ediliyordu. Bu bilgiler, JSF ve [faces-config.xml] yapılandırma dosyalarına taşınacaktır.

Şimdi [Application] bean'ine geçelim:

[1] dosyasındaki tüm hatalı satırları siliyoruz ve 13. ve 21. satırlardaki [IMetierLocal] arayüzünü [IMetier] olarak değiştiriyoruz. [2]'te artık hata yok. [1]'te ise [Application] sınıfını application. kapsamındaki bir bean haline getiren 15-16. satırları sildik. Bu bilgi, JSF ve [faces-config.xml] yapılandırma dosyalarına taşınacaktır. Ayrıca, bean’e [métier] katmanından bir referans ekleyen 20. satırı da sildik. Artık bu referans Spring tarafından başlatılacaktır. Gerekli yapılandırma dosyasına zaten sahibiz; bu, Spring / Métier projesine ait dosyadır. Bu dosyayı kopyalıyoruz:

  • dosyasını [1, 2]'e kopyalıyoruz; Spring / Métier projesindeki Spring yapılandırma dosyasını Spring / JSF projesine kopyalıyoruz,

[3]'te sonuç.

[Application] bean'inde, [métier] katmanına referans sağlamak için bu yapılandırma dosyasını kullanmamız gerekiyor. Bu, [init] yönteminde yapılır:


package beans;

...
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {

  // iş katmanı
  private IMetier metier;
...

  public Application() {
  }

  @PostConstruct
  public void init() {
    try {
      // katman örneği [métier]
      ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-metier-dao.xml");
      metier = (IMetier) ctx.getBean("metier");
      // doktorlar ve müşteriler önbelleğe alınır
...
    } catch (Throwable th) {
...
    }
...
  }
  • 20. satır: Spring yapılandırma dosyasındaki bean'ler örneklenir,
  • 21. satır: iş mantığı bean'ine, yani [métier] katmanına bir referans istenir.

Genel olarak, Spring bean'lerinin örneklenmesi, uygulama kapsamındaki bean'in init yönteminde yapılmalıdır. Bean'lerin örneklenmesinin bir Spring servlet'i tarafından yapıldığı başka bir yöntem de mevcuttur. Bu, [web.xml] dosyasının değiştirilmesini ve [spring-web] artefaktına bir bağımlılık eklenmesini gerektirir. Önceki kodlarda kullanılan yaklaşımla tutarlılık sağlamak amacıyla burada bu değişikliği yapmadık.

[Application] ve [Form] sınıflarındaki, bu sınıfları JSF bean'leri haline getiren anotasyonları kaldırdık. Bu sınıflar, JSF bean'leri olarak kalmalıdır. Anotasyonlar yerine, bean'leri tanımlamak için JSF ve [WEB-INF / faces.config.xml] yapılandırma dosyaları kullanılır.

Bu dosya artık şu şekildedir:


<?xml version='1.0' encoding='UTF-8'?>

<!-- =========== FULL CONFIGURATION FILE ================================== -->

<faces-config version="2.0"
              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 http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

  <application>
    <!-- mesaj dosyası -->
    <resource-bundle>
      <base-name>
        messages
      </base-name>
      <var>msg</var>
    </resource-bundle>
    <message-bundle>messages</message-bundle>
  </application>
    <!-- applicationBean bean'i -->
    <managed-bean>
      <managed-bean-name>applicationBean</managed-bean-name>
      <managed-bean-class>beans.Application</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope>
    </managed-bean>
    <!-- form bean'i -->
    <managed-bean>
      <managed-bean-name>form</managed-bean-name>
      <managed-bean-class>beans.Form</managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>
      <managed-property>
        <property-name>application</property-name>
        <value>#{applicationBean}</value>
      </managed-property>
    </managed-bean>
</faces-config>
  • 10-19. satırlar mesaj dosyasını yapılandırır. Bu, JSF / EJB projesinde sahip olduğumuz tek yapılandırmaydı,
  • 21-35. satırlar, JSF uygulamasının bean'lerini tanımlar. Bu, JSF 1.x sürümünde kullanılan standart yöntemdi. JSF 2 sürümü anotasyonları getirmiştir, ancak JSF ve 1.x sürümlerindeki yöntem hâlâ desteklenmektedir,
  • 21-25. satırlar: applicationBean bean'ini bildirir,
  • 22. satır: bean'in adı. "application" adını kullanmak cazip gelebilir. Ancak bu, JSF'te önceden tanımlanmış bir bean'in adı olduğu için kaçınılmalıdır,
  • 23. satır: bean sınıfının tam adı,
  • 24. satır: bean'in kapsamı,
  • 27-35. satırlar: form bean'ini tanımlar,
  • 28. satır: bean'in adı,
  • 29. satır: bean sınıfının tam adı,
  • satır 30: kapsamı,
  • satır 31-34: [beans.Form] sınıfının bir özelliğini tanımlar,
  • 32. satır: Özelliğin adı. [beans.Form] sınıfında bu isimde bir alan ve buna ait setter bulunmalıdır,
  • satır 33: alanın değeri. Burada, 21. satırda tanımlanan applicationBean bean'ine yapılan referans yer almaktadır. Burada, application kapsamındaki bean'in, session kapsamındaki bean'e enjekte edilmesi gerçekleştirilmektedir; böylece bu bean, application kapsamındaki verilere erişebilir.

Yukarıda, [beans.Form] bean'inin [application] alanının bir setter aracılığıyla başlatılacağını belirtmiştik. Dolayısıyla, bu alan henüz mevcut değilse [beans.Form] sınıfına eklenmelidir:


public void setApplication(Application application) {
    this.application = application;
  }

4.3.5. Uygulamanın test edilmesi

Uygulamamız artık hatasız ve testlere hazır:

  • [1]'te, düzeltilmiş proje,
  • [2] dosyasında derlenir,
  • [3]'te çalıştırıyoruz. SGBD ve MySQL'in başlatılmış olması gerekir. Tomcat sunucusu, henüz başlatılmamışsa [4] ile başlatılacak, ardından uygulamanın ana sayfası [5] olarak görüntülenecektir:

Bu noktadan itibaren, incelediğimiz uygulamayı görüyoruz. Uygulamanın çalıştığını okuyucunun kendisinin kontrol etmesini bırakıyoruz. Şimdi uygulamayı durduralım:

  • [1]'te uygulamayı kapatırız,
  • [2]'te ise uygulama artık mevcut değildir.

Şimdi Tomcat’ın günlüklerine bakalım:

1
2
3
4
5
6
mai 25, 2012 2:15:57 PM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
Grave: The web application [/mv-rdvmedecins-spring-jsf2] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
mai 25, 2012 2:15:57 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
Grave: The web application [/mv-rdvmedecins-spring-jsf2] appears to have started a thread named [MySQL Statement Cancellation Timer] but has failed to stop it. This is very likely to create a memory leak.
mai 25, 2012 2:15:59 PM org.apache.catalina.startup.HostConfig checkResources
Infos: Repli (undeploy) de l'application web ayant pour chemin de contexte /mv-rdvmedecins-spring-jsf2

2. ve 4. satırlar, uygulamanın kapatılması sırasında bir arıza olduğunu gösteriyor. 4. satır, muhtemel bir bellek sızıntısı riski olduğunu belirtiyor. Nitekim bu durum gerçekleşiyor ve bir süre sonra NetBeans kullanılamaz hale geliyor. Bu sorun özellikle can sıkıcıdır, çünkü projenin her yeni çalıştırılışında NetBeans'i yeniden başlatmak gerekiyor. Bu sorun, "Örneklerle Struts 2'ye Giriş" başlıklı [http://tahe.developpez.com/java/struts2] belgesinde de daha önce ele alınmıştı.

İnternette bu hatayla ilgili pek çok bilgi bulunabilir. Bu hata, bir uygulamayı Tomcat’a tekrar tekrar yükleyip kaldırdığımızda ortaya çıkar. Bir süre sonra java.lang.OutOfMemoryError: PermGen space hatası alınır. Görünüşe göre, bu hatanın kaynağı burada olduğu gibi üçüncü taraf arşivleri (jar) olduğunda, bu hatayı önlemenin bir yolu yoktur. Bu durumda, hatayı ortadan kaldırmak için Tomcat'i yeniden başlatmak gerekir.

Ancak bu hatanın ortaya çıkmasını geciktirmek mümkündür. Öncelikle, taşma yaşanan bellek alanını genişletiriz.

  • [1]'te, Tomcat sunucusunun özelliklerine gidip,
  • [2]'te, [Platform] sekmesinde, taşma bellek değerini ayarlıyoruz. Burada, toplam bellek 8 GB olduğu için 1 GB değerini girdik. Daha küçük bir bellek kapasitesi varsa, 512M (512 megabayt) değerini girebiliriz.

Ardından, MySQL'teki JDBC sürücüsünü, <tomcat>/lib konumuna yerleştirin; burada <tomcat>, Tomcat'in kurulum dizinidir.

  • dosyasını [1] dosyasına kopyalayın; Tomcat özelliklerinde kurulum dizini olarak <tomcat>'i not edin,
  • <tomcat>/lib dizinine [2], MySQL ve [3] sürümlerinden daha yeni bir sürücü olan JDBC yerleştirilir.

Ardından, projenin JDBC sürücüsüne olan bağımlılığını kaldırın (MySQL ve [4] sürücülerinden).

Bu işlem tamamlandıktan sonra uygulamayı test ediyoruz. Uygulamanın tekrar tekrar yüklenip kaldırılabildiğini görüyoruz. Ancak bellek sızıntısı sorunları çözülmemiştir. Bu sorunlar sadece daha sonra ortaya çıkmaktadır.

4.4. Conclusion

JSF / EJB / Glassfish uygulamasını bir JSF / Spring / Tomcat ortamına taşıdık. Bu işlem, esasen iki proje arasında kopyala-yapıştır yoluyla gerçekleştirildi. Bu, Spring ve EJB3 teknolojilerinin birbirine büyük ölçüde benzemesi sayesinde mümkün oldu. Nitekim EJB3, Spring'in EJB2'ten daha yüksek performans gösterdiği anlaşıldıktan sonra geliştirilmişti. EJB3, Spring’in başarılı fikirlerini benimsemiştir.

4.5. Eclipse ile yapılan testler

  • [1]'te üç Spring projesini içe aktarıyoruz,
  • [2]'te, [DAO] katmanından JUnit testini seçip çalıştırıyoruz [3]'te,
  • [4]'te test başarılı olur,
  • [5]'te konsol günlükleri.
  • [6A]'te [6B]'te, [métier] katmanının konsol istemcisi çalıştırılıyor,
  • [7]'te elde edilen konsol ekranı,
  • [8] ve [9]'te, web projesi bir Tomcat 7 sunucusunda çalıştırılır ([10]),
  • ve [11] adreslerinde, uygulamanın ana sayfası Eclipse'in dahili tarayıcısında görüntülenir.