20. RxJava ile asenkron programlama
Okunması gereken belge: [Introduction à RxJava. Application aux environnements Swing et Android.]
Bu bölümde, aşağıdaki mimariye sahip bir istemci/sunucu uygulaması oluşturduğumuz 17.6. bölüme geri dönüyoruz:
![]() |
[1]'teki Swing arayüzünde kullanıcının gerçekleştirdiği bazı eylemler, HTTP ve [2] ağları üzerinden [3]'teki veritabanında eylemleri tetiklemektedir. Bu nedenle, kullanıcının eylemine verilen yanıtın gelmesi biraz zaman alabilir. Kullanıcı arayüzüne bir bekleme göstergesi eklenmesi ve işlemin çok uzun sürmesi durumunda başlatılan işlemi iptal etme seçeneği sunulması iyi olurdu. 17.6. bölümde, sunucuyla bilgi alışverişi gerektiren her kullanıcı eylemi senkronize olarak gerçekleştirilir. Kod tarafından yürütülen olay işleyicisi, yanıt alınana kadar tamamlanmaz. Bu süre boyunca grafik kullanıcı arayüzü donar: kullanıcının yeni eylemlerine yanıt vermez. Bu eylemler, o anda çalışmakta olan olay işleyicisi tamamlandığında işlenmek üzere basitçe kuyruğa alınır. Dolayısıyla, bir iptal düğmesi görüntülenirse, kullanıcı bu düğmeye tıklayabilir ancak devam eden işlem tamamlanana kadar hiçbir şey olmaz. Bu durumda iptal düğmesinin hiçbir anlamı kalmaz.
İptal düğmesine tıklamanın bir sonuç doğurması için, devam eden işlemin tamamlanmış olması gerekir. Bunun için, potansiyel olarak uzun sürecek işlemi asenkron olarak başlatmalıdır:
- olay işleyicisi uzun süren işlemi başlatır ancak sonucunu beklemez ve kontrolü, grafik kullanıcı arayüzündeki olayları yöneten UI iş parçacığına geri verir. Uzun süren işlem, UI iş parçacığından farklı bir iş parçacığı üzerinde başlatılır; bu sayede UI iş parçacığı engellenmez;
- kullanıcı, uzun süren işlemin tamamlanmasından önce iptal düğmesine tıklarsa, boş durumda olan UI iş parçacığı bu olayı işleyebilir. Böylece, uzun süren işlemin sonucunu göz ardı ederek işlemden vazgeçilebilir;
- uzun işlem iptal edilmediyse, yanıtın gelmesi UI iş parçacığında bir olay tetikleyecektir. Bu iş parçacığı, boş durumda ise, yanıtı işleyecek olan bu olayla ilişkili kodu çalıştıracaktır;
Kullanıcı arayüzü daha önce olduğu gibi çalışacaktır. Sunucunun yanıt süreleri hızlıysa, kullanıcı farkı hissetmeyecektir. Yanıt süreleri fark edilebilir düzeydeyse, kullanıcı bir iptal düğmesinin belirdiğini görecek ve devam eden işlemi durdurma imkânına sahip olacaktır.
[Rx] kütüphanesi, asenkron programlama yapılmasına olanak tanır. En büyük avantajı, birçok ortama (Java, .NET, JS, ...) ve bir ortamda edinilen bilgi birikiminin başka bir ortama kolayca aktarılabilmesidir. Burada [Introduction à RxJava. Application aux environnements Swing et Android] belgesinin 2. bölümünden yararlanacağız. Okuyucunun bu bölümü okumasını öneririz. Aşağıda, bu bölümdeki örneklerden alınan kodları kullanacağız.
Uygulamanın mimarisini şu şekilde geliştireceğiz:
![]() |
- [1]'e; [swing] katmanı ile [métier] katmanı arasına bir [RxJava] katmanı ekleyeceğiz. Bu katmandaki yöntemler artık asenkron olarak çağrılacaktır;
İşlemi birkaç adımda gerçekleştireceğiz:
- 1. adım: [metier, DAO] katmanı şu anda [ui] katmanına senkron bir arayüz sunuyor. Bunu asenkron bir [RxJava, metier, DAO] katmanına dönüştüreceğiz;
- 2. adım: Senkron konsol uygulamasını, yine senkron ancak [RxJava, metier, DAO] asenkron arayüzünü kullanan bir uygulamaya dönüştüreceğiz;
- 3. adım: Senkron Swing uygulamasını asenkron bir Swing uygulamasına dönüştüreceğiz;
20.1. 1. adım
Mevcut senkron katmanı [metier, DAO]'i asenkron katman [RxJava, metier, DAO]'e dönüştürüyoruz.
20.1.1. Oluşturma
17.4. bölümdeki Maven projesinden yola çıkıyoruz ve bunu NetBeans ile açıyoruz:
![]() | ![]() |
Bu [1] projesini (kopyala/yapıştır) yeni bir proje olan [elections-rxjava-metier-dao-security-webjson] [2]'e kopyalıyoruz.
20.1.2. Maven Yapılandırması
Yeni projenin [pom.xml] dosyasını güncelleyerek [RxJava] kütüphanesine bağımlılık ekliyoruz:
<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.elections</groupId>
<artifactId>elections-metier-dao-security-rxjava-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>Client jUnit du serveur web / jSON</description>
<name>elections-metier-dao-security-rxjava-webjson</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- Spring tarafından kullanılan jSON kütüphanesi -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Spring tarafından kullanılan bileşen RestTemplate -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- Google Guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0.1</version>
<scope>test</scope>
</dependency>
<!-- günlük kütüphanesi -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<!-- Spring Boot Testi -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.reactivex/rxjava -->
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
- 65-70. satırlar: RxJava kütüphanesine bağımlılık ekledik;
20.1.3. [métier] katmanının asenkron uygulaması
![]() |
[RxJava, métier] katmanını uygulamak için, projeye [IRxElectionsMetier] [1] asenkron arayüzünü ve bunun [RxElectionsMetier] [2] uygulamasını ekliyoruz:
![]() |
[IRxElectionsMetier] arayüzü, [RxJava, métier] katmanının asenkron arayüzüdür. Kod şöyledir:
package elections.security.client.metier;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import rx.Observable;
public interface IRxElectionsMetier {
// kimlik doğrulama
Observable<Void> authenticate(User user);
// yarışmada yer alan listeleri almak
Observable<ListeElectorale[]> getListesElectorales(User user);
// doldurulacak koltuk sayısı
Observable<Integer> getNbSiegesAPourvoir(User user);
// seçim barajı
Observable<Double> getSeuilElectoral(User user);
// sonuçların kaydı
Observable<Void> recordResultats(User user, ListeElectorale[] listesElectorales);
// koltuk hesaplaması
Observable<ListeElectorale[]> calculerSieges(User user, ListeElectorale[] listesElectorales);
}
[IRxElectionsMetier] arayüzü, [IElectionsMetier] arayüzünün yöntemlerini devralır; ancak [IElectionsMetier] arayüzündeki bir M yöntemi T türünde bir sonuç döndürürken, [IRxElectionsMetier] arayüzündeki M yöntemi, Observable<T> türünde bir sonuç döndürür. [Observable] türü, RxJava kütüphanesi tarafından sağlanır. Bir Observable<T> türü, T türünü asenkron olarak alacak olan [subscribe] yöntemini sağlar. Bu yöntemle ilişkili üç olay vardır:
- onSuccess(T result), T türünde bir sonucun hazır olduğunu bildirir. Asenkron işlem birden fazla sonuç verebilir;
- onError(Throwable th): Asenkron işlemin bir hatayla karşılaştığını bildirir;
- onCompleted(): Asenkron işlemin tamamlandığını bildirir;
[Observable.subscribe] yöntemi çağrılmadıkça, gözlemlenebilir ile ilişkili asenkron işlem başlatılmaz. [IRxElectionsMetier] arayüzünün M yöntemini çağıran kod, beklenen T sonucunu almaz, ancak daha sonra [Observable.subscribe] yöntemini çağırarak T sonucunu almasını sağlayacak bir Observable<T> türü alır.
[IRxElectionsMetier] arayüzünün [RxElectionsMetier] uygulaması şu şekildedir:
package elections.security.client.metier;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import rx.Observable;
@Component
public class RxElectionsMetier implements IRxElectionsMetier {
@Autowired
private IElectionsMetier metier;
@Override
public Observable<Void> authenticate(User user) {
...
}
@Override
public Observable<ListeElectorale[]> getListesElectorales(User user) {
return Observable.create(subscriber -> {
try {
// senkron yöntem çağrısı ve ardından aboneye yanıt
subscriber.onNext(metier.getListesElectorales(user));
// gözlemlenebilirin sonu bildirilir
subscriber.onCompleted();
} catch (Exception e) {
// istisna iletilir
subscriber.onError(e);
}
});
}
@Override
public Observable<Integer> getNbSiegesAPourvoir(User user) {
...
}
@Override
public Observable<Double> getSeuilElectoral(User user) {
...
}
@Override
public Observable<Void> recordResultats(User user, ListeElectorale[] listesElectorales) {
...
}
@Override
public Observable<ListeElectorale[]> calculerSieges(User user, ListeElectorale[] listesElectorales) {
...
}
}
- 12-13. satırlar: senkron iş katmanının Spring enjeksiyonu;
- 20-34. satırlar: [ListeElectorale[]] türü yerine [Observable<ListeElectorale[]>] türünü döndüren [getListesElectorales] yöntemini yorumlayacağız;
- satır 22-32: statik [Observable.create] yöntemi, bir [Subscriber] türünden bir Observable oluşturulmasına olanak tanır. [Subscriber] türü, gözlemlenen süreç (Observable) tarafından üretilen sonuç akışlarına abone olan bir nesneyi temsil eder. Bu tür üç yöntem sunar:
- [Subscriber.onNext] (satır 25), gözlemlenen süreçten bir sonuç almak için;
- [Subscriber.onError] (satır 30), gözlemlenen süreçten bir istisna almak için kullanılır. Bir istisna sonrasında, [Observable] türü artık sonuç yaymaz;
- [Subscriber.onCompleted] (satır 27), gözlemlenen sürecin yayın sonu sinyalini almak için kullanılır. Burada, gözlemlenen süreç yalnızca bir eleman yayınlar. Bir istisna meydana gelirse bu sinyalin yayınlanmayacağına dikkat edilmelidir. Bu, Gözlemlenebilirlerin varsayılan davranışıdır: bir istisnanın yayınlanması, aynı zamanda yayınların sonunu da işaret eder. Aboneler bunu bilir;
- 22-34. satırlar: [Observable.create] yöntemi, parametre olarak [Observable.OnSubscribe] türünü kabul eder. Bu tür, işlevsel bir arayüzdür. Bu kavram Java 8 ile getirilmiştir ve tek bir yönteme sahip bir arayüzü ifade eder. Burada, [Observable.OnSubscribe] arayüzünün tek yöntemi şöyledir:
Tek yöntemli m(param1, param2, ..., paramn) işlevsel bir arayüzü uygulamak için aşağıdaki basitleştirilmiş sözdizimi kullanılabilir:
22-34. satırlarda yapılan da budur:
- [subscriber], [Observable.OnSubscribe.call] yönteminin parametresidir;
- 23-32. satırlar: [call] yöntemine verilmek istenen kod;
- 25. satır: 13. satırda enjekte edilen [métier] katmanından seçim listeleri senkronize bir şekilde talep edilir. Dolayısıyla sonuç beklenecektir. Sonuç alındığında, abonenin [onNext] yöntemine aktarılır;
- 28. satır: Hata durumunda, istisna abonenin [onError] yöntemine aktarılır;
- satır 31: Tek bir sonuç beklenir. Sonuç elde edildiğinde (seçmen listeleri veya bir istisna), aboneye gözlemlenen sürecin sonuç vermeyi tamamladığı bildirilir;
[RxElectionsMetier] yönteminin, ListeElectorale[] türünü değil, Observable<ListeElectorale[]> türünü döndürdüğü unutulmamalıdır. 23-33. satırlardaki kodun çalıştırılması ve 25. satır aracılığıyla seçmen listelerinin döndürülmesi için, çağıran kodun Observable<ListeElectorale[]>.subscribe yöntemini çağırması gerekir.
Diğer yöntemlerin kodları da benzerdir:
package elections.security.client.metier;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import rx.Observable;
@Component
public class RxElectionsMetier implements IRxElectionsMetier {
@Autowired
private IElectionsMetier metier;
@Override
public Observable<Void> authenticate(User user) {
return Observable.create(subscriber -> {
try {
// senkron yöntem çağrısı
metier.authenticate(user);
// gözlemlenebilirin sonu bildirilir
subscriber.onCompleted();
} catch (Exception e) {
// istisna iletiliyor
subscriber.onError(e);
}
});
}
@Override
public Observable<ListeElectorale[]> getListesElectorales(User user) {
return Observable.create(subscriber -> {
try {
// senkron yöntem çağrısı ve ardından aboneye yanıt
subscriber.onNext(metier.getListesElectorales(user));
// gözlemlenebilirin sonu bildirilir
subscriber.onCompleted();
} catch (Exception e) {
// istisna iletilir
subscriber.onError(e);
}
});
}
@Override
public Observable<Integer> getNbSiegesAPourvoir(User user) {
return Observable.create(subscriber -> {
try {
// senkron yöntem çağrısı ve ardından aboneye yanıt
subscriber.onNext(metier.getNbSiegesAPourvoir(user));
// gözlemlenebilirin sonu bildirilir
subscriber.onCompleted();
} catch (Exception e) {
// istisna iletilir
subscriber.onError(e);
}
});
}
@Override
public Observable<Double> getSeuilElectoral(User user) {
return Observable.create(subscriber -> {
try {
// senkron yöntem çağrısı ve ardından aboneye yanıt
subscriber.onNext(metier.getSeuilElectoral(user));
// gözlemlenebilirin sonu bildirilir
subscriber.onCompleted();
} catch (Exception e) {
// istisna iletilir
subscriber.onError(e);
}
});
}
@Override
public Observable<Void> recordResultats(User user, ListeElectorale[] listesElectorales) {
return Observable.create(subscriber -> {
try {
// senkron yöntem çağrısı
metier.recordResultats(user, listesElectorales);
// gözlemlenebilirin sonu bildirilir
subscriber.onCompleted();
} catch (Exception e) {
// istisna iletiliyor
subscriber.onError(e);
}
});
}
@Override
public Observable<ListeElectorale[]> calculerSieges(User user, ListeElectorale[] listesElectorales) {
return Observable.create(subscriber -> {
try {
// senkron yöntem çağrısı ve ardından aboneye yanıt
subscriber.onNext(metier.calculerSieges(user, listesElectorales));
// gözlemlenebilirin sonu bildirilir
subscriber.onCompleted();
} catch (Exception e) {
// istisna iletilir
subscriber.onError(e);
}
});
}
}
- 20. ve 81. satırlar: abone, sonuç beklemediği için [onNext] yöntemi çağrılmaz;
20.1.4. [métier] katmanındaki JUnit testleri
![]() |
20.1.4.1. Test01
17.4.4. paragrafında incelenen [Test01] birim testine geri dönüyoruz. Bu test, [IElectionsMetier] arayüzüne senkron çağrılar yapmak üzere tasarlanmıştır. Bu testi, yeni [IRxElectionsMetier] arayüzüne senkron çağrılar yapacak şekilde değiştiriyoruz. Aslında, asenkron bir arayüze (RxJava) senkron çağrılar yapmak mümkündür. Kod şu şekilde olur:
package elections.security.client.metier.junit;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import elections.security.client.config.MetierConfig;
import elections.security.client.entities.ElectionsException;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import elections.security.client.metier.IRxElectionsMetier;
import rx.observables.BlockingObservable;
@SpringApplicationConfiguration(classes = MetierConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
// katman [electionsMetier]
@Autowired
private IRxElectionsMetier electionsMetier;
// jSON eşleştiricisi
private final ObjectMapper mapper = new ObjectMapper();
// kullanıcılar
static private User admin;
static private User user;
static private User unknown;
@BeforeClass
public static void initTest() {
admin = new User("admin", "admin");
user = new User("user", "user");
unknown = new User("x", "y");
}
@Test()
public void checkUserUser() {
ElectionsException se = null;
try {
BlockingObservable.from(electionsMetier.authenticate(user)).firstOrDefault(null);
} catch (ElectionsException e) {
se = e;
}
Assert.assertNotNull(se);
Assert.assertEquals("403 Forbidden", se.getErreurs().get(0));
}
@Test()
public void checkUserUnknown() {
ElectionsException se = null;
try {
BlockingObservable.from(electionsMetier.authenticate(unknown)).firstOrDefault(null);
} catch (ElectionsException e) {
se = e;
}
Assert.assertNotNull(se);
Assert.assertEquals("401 Unauthorized", se.getErreurs().get(0));
}
@Test()
public void checkUserAdmin() {
ElectionsException se = null;
try {
BlockingObservable.from(electionsMetier.authenticate(admin)).firstOrDefault(null);
} catch (ElectionsException e) {
se = e;
}
Assert.assertNull(se);
}
/**
* vérification 1 : méthode de calcul des sièges on fixe en dur les listes
*/
@Test
public void calculSieges1() {
// 7 aday listesinden oluşan tablo oluşturulur
ListeElectorale[] listes = new ListeElectorale[7];
listes[0] = new ListeElectorale("A", 32000, 0, false);
listes[1] = new ListeElectorale("B", 25000, 0, false);
listes[2] = new ListeElectorale("C", 16000, 0, false);
listes[3] = new ListeElectorale("D", 12000, 0, false);
listes[4] = new ListeElectorale("E", 8000, 0, false);
listes[5] = new ListeElectorale("F", 4500, 0, false);
listes[6] = new ListeElectorale("G", 2500, 0, false);
// her listenin koltuk sayısı hesaplanır
listes = BlockingObservable.from(electionsMetier.calculerSieges(admin, listes)).first();
// sonuçlar kontrol ediliyor
Assert.assertEquals(2, listes[0].getSieges());
Assert.assertFalse(listes[0].isElimine());
Assert.assertEquals(2, listes[1].getSieges());
Assert.assertFalse(listes[1].isElimine());
Assert.assertEquals(1, listes[2].getSieges());
Assert.assertFalse(listes[2].isElimine());
Assert.assertEquals(1, listes[3].getSieges());
Assert.assertFalse(listes[3].isElimine());
Assert.assertEquals(0, listes[4].getSieges());
Assert.assertFalse(listes[4].isElimine());
Assert.assertEquals(0, listes[5].getSieges());
Assert.assertTrue(listes[5].isElimine());
Assert.assertEquals(0, listes[6].getSieges());
Assert.assertTrue(listes[6].isElimine());
}
/**
* vérification 2 : méthode de calcul des sièges on demande les listes à la couche [metier] puis on fixe en dur les
* voix
*/
@Test
public void calculSieges2() {
// 7 aday listesinin tablosu oluşturulur
ListeElectorale[] listes = BlockingObservable.from(electionsMetier.getListesElectorales(admin)).first();
// oylar sabitlenir
listes[0].setVoix(32000);
listes[1].setVoix(25000);
listes[2].setVoix(16000);
listes[3].setVoix(12000);
listes[4].setVoix(8000);
listes[5].setVoix(4500);
listes[6].setVoix(2500);
// her bir listenin kazandığı sandalye sayısı hesaplanır
listes = BlockingObservable.from(electionsMetier.calculerSieges(admin, listes)).first();
// sonuçlar kontrol ediliyor
Assert.assertEquals(2, listes[0].getSieges());
Assert.assertFalse(listes[0].isElimine());
Assert.assertEquals(2, listes[1].getSieges());
Assert.assertFalse(listes[1].isElimine());
Assert.assertEquals(1, listes[2].getSieges());
Assert.assertFalse(listes[2].isElimine());
Assert.assertEquals(1, listes[3].getSieges());
Assert.assertFalse(listes[3].isElimine());
Assert.assertEquals(0, listes[4].getSieges());
Assert.assertFalse(listes[4].isElimine());
Assert.assertEquals(0, listes[5].getSieges());
Assert.assertTrue(listes[5].isElimine());
Assert.assertEquals(0, listes[6].getSieges());
Assert.assertTrue(listes[6].isElimine());
}
/**
* vérification 3 méthode de calcul des sièges on provoque une exception
*/
@Test(expected = ElectionsException.class)
public void calculSieges3() {
// her biri 1 oy alan 24 aday listesinden oluşan bir tablo oluşturulur
ListeElectorale[] listes = new ListeElectorale[25];
// 25 listenin hepsi aynı sayıda oy alacak (%4)
for (int i = 0; i < listes.length; i++) {
listes[i] = new ListeElectorale("Liste" + (i + 1), 1, 0, false);
}
// Milletvekili sandalye dağılımı hesaplaması - normalde şu sonuç çıkmalıdır: ElectionsException
// %5 seçim barajı ile
BlockingObservable.from(electionsMetier.calculerSieges(admin, listes)).first();
}
/**
* enregistrement des résultats de l'élection
*
* @throws JsonProcessingException
*/
@Test
public void ecritureResultatsElections() throws JsonProcessingException {
// 7 aday listenin tablosu oluşturulur
ListeElectorale[] listes = BlockingObservable.from(electionsMetier.getListesElectorales(admin)).first();
// oylar sabitlenir
listes[0].setVoix(32000);
listes[1].setVoix(25000);
listes[2].setVoix(16000);
listes[3].setVoix(12000);
listes[4].setVoix(8000);
listes[5].setVoix(4500);
listes[6].setVoix(2500);
// her listenin kazandığı sandalye sayısı hesaplanıyor
listes = BlockingObservable.from(electionsMetier.calculerSieges(admin, listes)).first();
// sonuçlar görüntülenir
for (int i = 0; i < listes.length; i++) {
System.out.println(mapper.writeValueAsString(listes[i]));
}
// sonuçlar veritabanına kaydedilir
BlockingObservable.from(electionsMetier.recordResultats(admin, listes)).firstOrDefault(null);
// sonuçlar kontrol edilir
listes = BlockingObservable.from(electionsMetier.getListesElectorales(admin)).first();
// sonuçlar görüntülenir
for (int i = 0; i < listes.length; i++) {
System.out.println(mapper.writeValueAsString(listes[i]));
}
Assert.assertEquals(2, listes[0].getSieges());
Assert.assertFalse(listes[0].isElimine());
Assert.assertEquals(2, listes[1].getSieges());
Assert.assertFalse(listes[1].isElimine());
Assert.assertEquals(1, listes[2].getSieges());
Assert.assertFalse(listes[2].isElimine());
Assert.assertEquals(1, listes[3].getSieges());
Assert.assertFalse(listes[3].isElimine());
Assert.assertEquals(0, listes[4].getSieges());
Assert.assertFalse(listes[4].isElimine());
Assert.assertEquals(0, listes[5].getSieges());
Assert.assertTrue(listes[5].isElimine());
Assert.assertEquals(0, listes[6].getSieges());
Assert.assertTrue(listes[6].isElimine());
}
}
Değişiklikleri inceleyelim:
- 48. satır: [BlockingObservable.from(Observable).first] statik yöntemi:
- [from]'in gözlemlenebilir parametresine abone olur;
- gözlemlenebilir ile ilişkili kodun yürütülmesini başlatır;
- ilk sonucu almayı bekler. Dolayısıyla bu senkron bir işlemdir;
Burada [firstOrDefault(null)] yöntemini kullanıyoruz çünkü [metier.authenticate] gözlemlenebilir, yürütüldüğünde sonuç döndürmüyor. Dolayısıyla, [firstOrDefault(null)] yönteminin sonucu null olacaktır; bu değer burada kullanılmayacaktır;
[métier] katmanını her çağırmak istediğimizde, kodun geri kalanında da bu şemayı kullanıyoruz.
[Test01] birim testi başarılı olmalıdır:
![]() |
Yapılması gereken iş: [Test01] testinin başarılı olduğunu doğrulamak.
20.1.4.2. Test02
[Test01] testini, artık [IRxElectionsMetier] asenkron arayüzünü, yöntemlerine asenkron çağrılar yaparak test edecek şekilde dönüştürüyoruz.
İlk testi inceleyelim:
// iş parçacıkları senkronizasyon semaforu
private CountDownLatch latch;
// -----------------------------------
private ElectionsException checkUserUserException;
@Test()
public void checkUserUser() throws InterruptedException {
// 1 değerindeki semafor
latch = new CountDownLatch((1));
// asenkron işlem
electionsMetier.authenticate(user).subscribeOn(Schedulers.io())
.subscribe((result) -> {
},
(th) -> {
checkUserUserException = (ElectionsException) th;
latch.countDown();
},
() -> {
latch.countDown();
});
// semafor bekleme
latch.await();
// sonuçların doğrulanması
Assert.assertNotNull(checkUserUserException);
Assert.assertEquals("403 Forbidden", checkUserUserException.getErreurs().get(0));
}
- 2. satır: Semafor, iş parçacıkları arasında senkronizasyon sağlamak için kullanılan bir araçtır. İş parçacıkları, paralel olarak çalışan yürütme akışlarıdır. Bir T1 görevini yürütmek için, [Thread1] iş parçacığı, [Thread2] iş parçacığı tarafından yürütülen bir T2 görevinin tamamlanmasını bekleyebilir. Bu durumda, [Thread2] iş parçacığının, T2 görevinin tamamlandığını belirten bir sinyal göndermesini bekler. İki iş parçacığı arasındaki bu senkronizasyonu yönetmenin çeşitli yolları vardır. Burada kullanılan yöntem şöyledir:
- 10. satır: [Thread1] iş parçacığı, değeri 1 olan bir semafor oluşturur;
- 12. satır: [Thread1] iş parçacığı, [Thread2] iş parçacığını oluşturur ve başlatır. Bu, şu sözdizimi ile gerçekleştirilir:
electionsMetier.authenticate(user).subscribeOn(Schedulers.io())
[Observable.subscribeOn] yöntemi, gözlemlenen işlemin üzerinde çalışacağı iş parçacığını belirler. [subscribeOn] parametresi bir iş parçacığı havuzudur. RxJava kütüphanesi, farklı durumlara uygun çeşitli havuzlar sunar. [Schedulers.io()] havuzu, ağ işlemleri için önerilen havuzdur;
- (devamı)
- 12-13. satırlar:
electionsMetier.authenticate(user).subscribeOn(Schedulers.io()).subscribe(...)
işlemi, [authenticate(user)] gözlemlenebilirinde kapsüllenmiş senkron işlemi yürütür. Ancak bu senkron işlem, [Thread1] iş parçacığından farklı bir iş parçacığında başlatıldığından, [Thread1] iş parçacığı [subscribe] yönteminden yanıt beklemeden bir sonraki komuta geçer;
- (devamı)
- 23. satır: [Thread1] iş parçacığı durur ve semaforun 0'a düşmesini bekler (şu anda 1'dir);
- 13-21. satırlar: [subscribe] yöntemi, parametre olarak üç lambda fonksiyonu alır:
- birinci lambda işlevi [(result)->{...}], gözlemlenebilir [authenticate(user)] bir [result] sonucu yayınladığında her seferinde çağrılır. Burada, bir işlem yapan ancak hiçbir sonuç yayınlamayan [authenticate(user)] adlı bir gözlemlenebilirimiz var. Dolayısıyla [(result)->{}] lambda fonksiyonu hiçbir zaman çağrılmayacaktır. Bu nedenle kod kısmı boştur: [{}];
- ikincisi olan [(th)->{...}] ise parametre olarak [Throwable] türünü alır. Bu, gözlemlenebilirin yürütülmesi sırasında bir istisna ile karşılaşıldığında çağrılır. Burada, [Throwable th] parametresini şu şekilde işliyoruz:
- 16. satır: Yürütülen gözlemlenebilir nesne yalnızca bu tür bir istisna ürettiği için, onu [ElectionsException] türündeki test sınıfının bir alanına kaydediyoruz;
- 17. satır: [Thread2] iş parçacığının işini tamamladığını belirtmek için semaforu 0'a ayarlıyoruz;
- Üçüncü [()->{...}], gözlemlenebilirin yayınlayacak öğesi kalmadığında çağrılır. Bu olayı şu şekilde işliyoruz:
- 20. satır: [Thread2] iş parçacığının işini tamamladığını belirtmek için semaforu 0'a ayarlıyoruz;
Bir istisna meydana gelirse üçüncü lambda işlevinin çağrılmayacağına dikkat edilmelidir. Bu nedenle, 17. satırda da semaforu 0'a ayarlamak zorunda kaldık;
- 25. satır: Bu satıra geldiğimizde, gözlemlenebilir nesne işini tamamlamıştır. Bu durumda, [Test01] testinde olduğu gibi aynı kontrolleri yapabiliriz;
Başka bir testi inceleyelim:
// -----------------------------------
private ElectionsException calculSieges1Exception;
private ListeElectorale[] listesCalculSieges1;
@Test
public void calculSieges1() throws InterruptedException {
// 7 aday listeden oluşan tablo oluşturuluyor
ListeElectorale[] listes = new ListeElectorale[7];
listes[0] = new ListeElectorale("A", 32000, 0, false);
listes[1] = new ListeElectorale("B", 25000, 0, false);
listes[2] = new ListeElectorale("C", 16000, 0, false);
listes[3] = new ListeElectorale("D", 12000, 0, false);
listes[4] = new ListeElectorale("E", 8000, 0, false);
listes[5] = new ListeElectorale("F", 4500, 0, false);
listes[6] = new ListeElectorale("G", 2500, 0, false);
// semafor 1'e ayarlandı
latch = new CountDownLatch((1));
// asenkron işlem
// her listenin koltuk sayısı hesaplanıyor
electionsMetier.calculerSieges(admin, listes).subscribeOn(Schedulers.io())
.subscribe((result) -> {
listesCalculSieges1 = result;
},
(th) -> {
calculSieges1Exception = (ElectionsException) th;
latch.countDown();
},
() -> {
latch.countDown();
});
// semafor bekleme
latch.await();
// sonuçlar kontrol ediliyor
Assert.assertNull(calculSieges1Exception);
Assert.assertEquals(2, listesCalculSieges1[0].getSieges());
Assert.assertFalse(listesCalculSieges1[0].isElimine());
Assert.assertEquals(2, listesCalculSieges1[1].getSieges());
Assert.assertFalse(listesCalculSieges1[1].isElimine());
Assert.assertEquals(1, listesCalculSieges1[2].getSieges());
Assert.assertFalse(listesCalculSieges1[2].isElimine());
Assert.assertEquals(1, listesCalculSieges1[3].getSieges());
Assert.assertFalse(listesCalculSieges1[3].isElimine());
Assert.assertEquals(0, listesCalculSieges1[4].getSieges());
Assert.assertFalse(listesCalculSieges1[4].isElimine());
Assert.assertEquals(0, listesCalculSieges1[5].getSieges());
Assert.assertTrue(listesCalculSieges1[5].isElimine());
Assert.assertEquals(0, listesCalculSieges1[6].getSieges());
Assert.assertTrue(listesCalculSieges1[6].isElimine());
}
- 20-30. satırlar: [electionsMetier.calculerSieges(admin, listes)] gözlemlenebilirinin asenkron yürütülmesi;
- 21-23. satırlar: gözlemlenebilirin yürütülmesi [ListeElectorale[]] türünde bir değer döndürür; bu değer, 3. satırdaki test sınıfının bir alanına kaydedilir;
- satır 34-48: Bu doğrulamalar, [Test01] testine ait doğrulamalardır; bunlara, istisna oluşmadığından emin olmak için satır 34'teki doğrulama eklenmiştir;
[Test02] testinin tamamı ders materyallerinde mevcuttur.
Yapılacak ödev: [Test02] testini çalıştırın ve testin başarılı olduğunu doğrulayın.
20.1.4.3. Test03
[Test03] testi, [Test01] testiyle aynı işlevi görür: [IRxElectionsMetier] arayüzünü, bu arayüze yapılan senkron çağrılarla test eder. Bu, [Test02] testinin iki ayrıntı dışında bir kopyasıdır:
- gözlemlenebilirler artık testleri çalıştıran iş parçacığından farklı bir iş parçacığında çalıştırılmıyor. [Thread1] iş parçacığı, bir gözlemlenebilirin [subscribe] yöntemini çalıştırdığında, bu yöntem de yine [Thread1] iş parçacığı üzerinde sunucuya yönelik bir HTTP işlemi başlatır. Böylece [subscribe] yönteminin tamamı senkron hale gelir;
- artık tek bir iş parçacığı kaldığı için iş parçacığı senkronizasyonu gereksiz hale gelir ve semafor ortadan kalkar;
İşte iki test örneği:
// -----------------------------------
private ElectionsException checkUserUserException;
@Test()
public void checkUserUser() throws InterruptedException {
// senkron işlem
electionsMetier.authenticate(user)
.subscribe((result) -> {
},
(th) -> {
checkUserUserException = (ElectionsException) th;
},
() -> {
});
// sonuçların doğrulanması
Assert.assertNotNull(checkUserUserException);
Assert.assertEquals("403 Forbidden", checkUserUserException.getErreurs().get(0));
}
- 7. satır: Varsayılan olarak, [electionsMetier.authenticate(user).subscribe] yöntemi çağıran kodun iş parçacığında yürütülür. Dolayısıyla senkron bir işlem söz konusudur;
// -----------------------------------
private ElectionsException calculSieges1Exception;
private ListeElectorale[] listesCalculSieges1;
@Test
public void calculSieges1() throws InterruptedException {
// 7 aday listesinin tablosu oluşturuluyor
ListeElectorale[] listes = new ListeElectorale[7];
listes[0] = new ListeElectorale("A", 32000, 0, false);
listes[1] = new ListeElectorale("B", 25000, 0, false);
listes[2] = new ListeElectorale("C", 16000, 0, false);
listes[3] = new ListeElectorale("D", 12000, 0, false);
listes[4] = new ListeElectorale("E", 8000, 0, false);
listes[5] = new ListeElectorale("F", 4500, 0, false);
listes[6] = new ListeElectorale("G", 2500, 0, false);
// senkron işlem
// her listenin sandalye sayısı hesaplanıyor
electionsMetier.calculerSieges(admin, listes)
.subscribe((result) -> {
listesCalculSieges1 = result;
},
(th) -> {
calculSieges1Exception = (ElectionsException) th;
},
() -> {
});
// sonuçlar kontrol ediliyor
Assert.assertNull(calculSieges1Exception);
Assert.assertEquals(2, listesCalculSieges1[0].getSieges());
Assert.assertFalse(listesCalculSieges1[0].isElimine());
Assert.assertEquals(2, listesCalculSieges1[1].getSieges());
Assert.assertFalse(listesCalculSieges1[1].isElimine());
Assert.assertEquals(1, listesCalculSieges1[2].getSieges());
Assert.assertFalse(listesCalculSieges1[2].isElimine());
Assert.assertEquals(1, listesCalculSieges1[3].getSieges());
Assert.assertFalse(listesCalculSieges1[3].isElimine());
Assert.assertEquals(0, listesCalculSieges1[4].getSieges());
Assert.assertFalse(listesCalculSieges1[4].isElimine());
Assert.assertEquals(0, listesCalculSieges1[5].getSieges());
Assert.assertTrue(listesCalculSieges1[5].isElimine());
Assert.assertEquals(0, listesCalculSieges1[6].getSieges());
Assert.assertTrue(listesCalculSieges1[6].isElimine());
}
Yapılması gereken iş: [Test03] testini çalıştırın ve başarılı olup olmadığını kontrol edin.
20.2. 2. adım
Şimdi, 17.5. bölümdeki senkron konsol uygulamasını, yine senkron ancak [RxJava, metier, DAO] asenkron arayüzünü kullanan bir uygulamaya dönüştürüyoruz;
![]() |
17.5. bölümdeki [elections-console-metier-dao-security-webjson] [1] projesinden yola çıkarak, bunu yeni bir proje olan [elections-console-rxjava- metier-dao-security-webjson] [2]'e kopyalıyoruz:
![]() | ![]() |
- [3-4]'e aktarıyoruz; yeni projede eski senkron katman [métier]'e olan bağımlılığı kaldırıyoruz;
![]() | ![]() |
- [5-9]'te, yeni asenkron [métier] katmanına bir bağımlılık eklenir;
![]() | ![]() |
- [10-14]'te, [ElectionsConsole] sınıfının adı [ElectionsConsole01] olarak değiştirilir;
Aynı şekilde, [BootElectionsConsole] sınıfının adı [BootElectionsConsole01] olarak değiştirilir:
![]() |
[BootElectionsConsole01] sınıfının mevcut kodu şöyledir:
package elections.security.client.boot;
import elections.security.client.console.IElectionsUI;
public class BootElectionsConsole01 extends AbstractBootElections{
public static void main(String[] arguments) {
new BootElectionsConsole01().run();
}
@Override
protected IElectionsUI getUI() {
return ctx.getBean("electionsConsole",IElectionsUI.class);
}
}
- 13. satır: [ElectionsConsole] sınıfının adını [ElectionsConsole01] olarak değiştirdiğimiz için, artık şu şekilde yazılmalıdır:
return ctx.getBean("electionsConsole01",IElectionsUI.class);
[ElectionsConsole01] sınıfının koduna geri dönelim:
@Component
public class ElectionsConsole01 implements IElectionsUI {
@Autowired
private IElectionsMetier electionsMetier;
@Autowired
private User admin;
@Override
public void run() {
// yarışan listeler
ListeElectorale[] listes;
// verilerin girilmesi
try (Scanner clavier = new Scanner(System.in)) {
// [metier] katmanından yarışan listeler isteniyor
listes = electionsMetier.getListesElectorales(admin);
...
// koltuk sayısı hesaplanıyor
listes=electionsMetier.calculerSieges(admin,listes);
// sonuçlar kaydediliyor
electionsMetier.recordResultats(admin,listes);
...
}
20.1.4.1 numaralı paragrafta yer alan [Test01] test örneğini takip edersek, 5, 17, 20 ve 22. satırlar şu şekilde değişecektir:
@Component
public class ElectionsConsole01 implements IElectionsUI {
@Autowired
private IRxElectionsMetier electionsMetier;
@Autowired
private User admin;
@Override
public void run() {
// yarışmadaki listeler
ListeElectorale[] listes;
// verilerin girilmesi
try (Scanner clavier = new Scanner(System.in)) {
// [metier] katmanından yarışan listeler isteniyor
listes = BlockingObservable.from(electionsMetier.getListesElectorales(admin)).first();
...
// koltuk sayısı hesaplanıyor
listes = BlockingObservable.from(electionsMetier.calculerSieges(admin, listes)).first();
// sonuçlar kaydediliyor
BlockingObservable.from(electionsMetier.recordResultats(admin, listes));
...
}
Yapılacak iş: Projeyi, [BootElectionsConsole01] sınıfını [SS, Heures travaillées, Jours travaillés] parametreleriyle çalıştıracak şekilde yapılandırın ve bu şekilde yapılandırılmış projenin çalıştırılmasının beklenen sonuçları verdiğini doğrulayın.
Yapılması gereken: Projeyi, [BootElectionsConsole02, ElectionsConsole02] çiftini çalıştıracak şekilde yapılandırın; burada [ElectionsConsole02] sınıfı, 20.1.4.2 paragrafındaki [Test02] test modeline göre yazılmış olmalıdır.
Yapılacak iş: 20.1.4.3. paragrafındaki [Test03] testinin şablonuna göre yazılmış olan [ElectionsConsole03] sınıfını içeren [BootElectionsConsole03, ElectionsConsole03] çiftini çalıştıracak şekilde projeyi yapılandırın.
20.3. 3. adım
Şimdi Swing uygulamasını asenkron bir ortama taşıma aşamasına geçiyoruz.
![]() |
İlk olarak, 17.6. bölümdeki [elections-swing-metier-dao-security-webjson] [1] projesini, yeni bir [elections-swing-rxjava-metier-dao-security-webjson] [2] projesine kopyalayacağız:
![]() | ![]() |
- [3, 4]'te, senkron [console] katmanına olan bağımlılığı kaldırıyoruz;
![]() | ![]() |
- [5-9]'te, asenkron konsol katmanına bir bağımlılık ekliyoruz;
[swing] katmanı, [métier] katmanına gerçek asenkron çağrılar yapacaktır. Bu katmandaki bir yönteme çağrı yapıldığında, iki iş parçacığı olacaktır:
- olayları yöneten UI iş parçacığı;
- sunucuya HTTP çağrısını gerçekleştirecek bir G/Ç iş parçacığı;
Asenkron çağrı süresince, bir bekleme görüntüsü ve bir iptal düğmesi göstermemiz gerekir. Bunu burada yapmayacağız ve bu, uygulamanın iyileştirilmesi için size önerilecektir. Değişiklikler, [métier] katmanına çağrı yapan iki sınıfta yapılır:
![]() |
20.3.1. Maven Yapılandırması
Burada, [RxJava] kütüphanesine yalnızca Swing ortamında kullanılabilen işlevler ekleyen [RxSwing] kütüphanesini kullanacağız. Bunun için [pom.xml] dosyasını şu şekilde değiştiriyoruz:
<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.elections</groupId>
<artifactId>elections-swing-rxjava-metier-dao-security-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>elections-swing-rxjava-metier-dao-security-webjson</name>
<description>couche swing asynchrone du client web / jSON</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- RxSwing -->
<!-- https://mvnrepository.com/artifact/io.reactivex/rxswing -->
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxswing</artifactId>
<version>0.27.0</version>
</dependency>
<!-- alt katmanlar -->
<dependency>
<groupId>istia.st.elections</groupId>
<artifactId>elections-console-rxjava-metier-dao-security-webjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
20.3.2. [ElectionsConnectForm] sınıfı
Asenkron çalışmada, [ElectionsConnectForm] sınıfı şu şekilde olur:
package elections.security.client.swing;
import elections.security.client.console.IElectionsUI;
import elections.security.client.entities.User;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.SwingUtilities;
import elections.security.client.metier.IRxElectionsMetier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import rx.schedulers.Schedulers;
import rx.schedulers.SwingScheduler;
@Component
public class ElectionsConnectForm extends AbstractElectionsConnectForm implements IElectionsUI {
private static final long serialVersionUID = 1L;
// [métier] asenkron katman referansı
@Autowired
private IRxElectionsMetier metier;
// oturum açmış kullanıcı
private User user;
// ana form
@Autowired
private ElectionsMainForm electionsMainForm;
// oturum UI
@Autowired
private UiSession uiSession;
@Override
protected void doConnect() {
if (isPageValid()) {
// kullanıcı kimlik doğrulaması
metier.authenticate(user).subscribeOn(Schedulers.io()).observeOn(SwingScheduler.getInstance()).subscribe(
// yanıt yok
(result) -> {
},
// istisna yönetimi
(th) -> {
// hata kaydediliyor
String info = getInfoForException("Les erreurs suivantes se sont produites :", th);
// bilgi görüntüleniyor
jTextPaneErreurs.setText(info);
jTextPaneErreurs.setCaretPosition(0);
},
// kimlik doğrulama tamamlandı
() -> {
// kullanıcı oturumda kaydedilir
uiSession.setUser(user);
// giriş ekranı gizlenir
setVisible(false);
// ana sayfa görüntüleniyor
electionsMainForm.run();
});
}
}
// başlatma işlemleri
@Override
protected void init() {
...
}
@Override
public void run() {
// grafik kullanıcı arayüzü görüntülenir
SwingUtilities.invokeLater(new Runnable() {
public void run() {
init();
setVisible(true);
}
});
}
private boolean isPageValid() {
...
}
private String getInfoForException(String message, Throwable ex) {
...
}
}
- 36-63. satırlar: Kullanıcı [Connexion] menü seçeneğine tıkladığında [doConnect] yöntemi yürütülür:
![]() |
Her şey 40. satırda yer almaktadır:
metier.authenticate(user).subscribeOn(Schedulers.io()).observeOn(SwingScheduler.getInstance()).subscribe(...)
- Gözlemlenen işlem [metier.authenticate(user)]'tir;
- bu işlem, [Schedulers.io()] havuzundan alınan bir G/Ç iş parçacığı üzerinde çalıştırılacaktır;
- UI iş parçacığında gözlemlenecek; bu iş parçacığı, Swing arayüzü [observeOn(SwingScheduler.getInstance())]'in olaylarını yönetir. Bu iş parçacığı, [SwingScheduler.getInstance()] yöntemi ile elde edilir; burada [SwingScheduler], [RxSwing] kütüphanesi tarafından sağlanan bir sınıftır. Bu zorunludur. Asenkron işlemin sonucu alındığında, bu sonuç genellikle Swing arayüzündeki öğeleri değiştirmek için kullanılır. Ancak bu arayüz yalnızca UI iş parçacığında değiştirilebilir; aksi takdirde bir istisna oluşur. Bu nedenle, 41-61. satırların UI iş parçacığında yürütülmesi gerekir. Bu, burada [observeOn(SwingScheduler.getInstance())] yöntemi ile sağlanmaktadır;
Kodun geri kalanını açıklayalım:
- 42-43. satırlar: Bu satırlar, [subscribe] yönteminin sözdizimine uymak için eklenmiştir. [metier.authenticate(user)] işlemi hiçbir sonuç döndürmediğinden, bu satırlar asla çalıştırılmayacaktır;
- 35-52. satırlar: Bir istisna alındığında, bu istisna görüntülenir;
- 54-61. satırlar: [metier.authenticate(user)] işlemi, veri gönderimini tamamladığını bildirdiğinde yürütülür;
20.3.3. [ElectionsMainForm] sınıfı
![]() |
20.3.3.1. Grafik kullanıcı arayüzünün başlatılması
package elections.security.client.swing;
import elections.security.client.console.IElectionsUI;
import elections.security.client.entities.ListeElectorale;
import elections.security.client.entities.User;
import elections.security.client.metier.IRxElectionsMetier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import rx.schedulers.Schedulers;
import rx.schedulers.SwingScheduler;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@Component
public class ElectionsMainForm extends AbstractElectionsMainForm implements IElectionsUI {
private static final long serialVersionUID = 1L;
// asenkron [métier] katmanına referans
@Autowired
private IRxElectionsMetier metier;
// UI oturumu
@Autowired
private UiSession uiSession;
// oturum açmış kullanıcı
private User user;
// JList listelerinin şablonları
private DefaultListModel<String> modèleNomsVoix = null;
private DefaultListModel<String> modèleRésultats = null;
// yarışan listeler
private ListeElectorale[] listes;
// kullanıcı tarafından girilen listeler
private final List<ListeElectorale> listesSaisies = new ArrayList<>();
private ListeElectorale[] tListesSaisies;
// başlatma işlemleri
@Override
protected void init() {
// üst sınıf tarafından bileşenlerin oluşturulması
super.init();
// form durumu
Utilitaires.setEnabled(new JLabel[]{jLabelAjouter, jLabelCalculer, jLabelEnregistrer, jLabelSupprimer}, false);
Utilitaires.setEnabled(
new JMenuItem[]{jMenuItemAjouter, jMenuItemCalculer, jMenuItemEnregistrer, jMenuItemSupprimer}, false);
// pencereyi ortala
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
// oturum açmış kullanıcı
user = uiSession.getUser();
// yerel başlatmalar
modèleNomsVoix = new DefaultListModel<>();
jListNomsVoix.setModel(modèleNomsVoix);
modèleRésultats = new DefaultListModel<>();
jListResultats.setModel(modèleRésultats);
// [métier] katmanından listeler isteniyor
metier.getListesElectorales(user).subscribeOn(Schedulers.io()).observeOn(SwingScheduler.getInstance())
.subscribe(
// yanıt
listesElectorales -> {
// listeler kaydediliyor
listes = listesElectorales;
},
// istisna
(th) -> showException(th),
// gözlemlenebilir son
() -> {
// sonraki adım
doInitStep2();
});
}
...
- 46. satır: [init] yöntemi, ilgili pencere görüntülenecekken yürütülür. Bu yöntemin amacı, aşağıdaki [1-3] bileşenlerini başlatmaktır:
![]() |
- 71-85. satırlar: Aday listeler asenkron olarak istenir ([1] bileşeni);
- 71. satır: Gözlemlenen işlem [metier.getListesElectorales(user)]'tir. Bu işlem, [subscribeOn(Schedulers.io())] G/Ç iş parçacığı üzerinde yürütülür ve UI ile [observeOn(SwingScheduler.getInstance()] iş parçacıkları üzerinden gözlemlenir;
- 74-77. satırlar: Gözlemlenen işlem tarafından döndürülen sonuç, 38. satırdaki [listes] alanına kaydedilir;
- 79. satır: Olası bir istisna, aşağıdaki yöntemle işlenir:
private void showException(Throwable th) {
// istisna görüntüleniyor
jTextPaneMessages.setText(getInfoForException("Les erreurs suivantes se sont produites : ", th));
jTextPaneMessages.setCaretPosition(0);
}
- satır 81-84: Gözlemlenen işlemin sonunda, satır 81-84 yürütülür. Bir istisna meydana gelmişse bu satırlar yürütülmez. [doInitStep2] yöntemi, başlatmanın 2. adımını şu şekilde gerçekleştirir:
private void doInitStep2() {
// liste adlarını açılır menüye eşleştirme jComboBoxNomsListes
for (int i = 0; i < listes.length; i++) {
jComboBoxNomsListes.addItem(String.format("%s - %s", listes[i].getId(), listes[i].getNom()));
}
// doldurulacak koltuk sayısı
metier.getNbSiegesAPourvoir(user).subscribeOn(Schedulers.io()).observeOn(SwingScheduler.getInstance())
.subscribe(
// yanıt
nbSiegesAPourvoir -> {
// bu bilgiyle ilişkili etiket başlatılır
jLabelSAP.setText(jLabelSAP.getText() + nbSiegesAPourvoir);
},
// istisna
(th) -> showException(th),
// gözlemlenebilir son
() -> {
// sonraki adım
doInitStep3();
});
}
- satır 3-5: Bir önceki adımın sonucu kullanılarak açılır menü, aday listelerin adlarıyla doldurulur;
- 7-20. satırlar: doldurulacak koltuk sayısı asenkron olarak istenir;
- 7. satır: Gözlemlenen işlem [metier.getNbSiegesAPourvoir(user)]'tir. Bu işlem, [subscribeOn(Schedulers.io())] G/Ç iş parçacığı üzerinde yürütülür ve UI ile [observeOn(SwingScheduler.getInstance()] iş parçacıkları üzerinden gözlemlenir;
- 10-13. satırlar: İşlem tarafından döndürülen sonuç, grafik kullanıcı arayüzünü güncellemek için kullanılır;
- 15. satır: Olası bir istisna görüntülenir;
- satır 17-20: gözlemlenebilirin bitiş sinyali alındığında, başlatma sürecinin 3. aşamasına geçilir;
Başlatma işleminin 3. adımı aşağıdaki kodla gerçekleştirilir:
private void doInitStep3() {
// seçim barajı
metier.getSeuilElectoral(user).subscribeOn(Schedulers.io()).observeOn(SwingScheduler.getInstance())
.subscribe(
// yanıt
seuilElectoral -> {
// bu bilgiyle ilişkili etiketi başlatıyoruz
jLabelSE.setText(jLabelSE.getText() + seuilElectoral);
},
// istisna
(th) -> showException(th),
// gözlemlenebilir son
() -> {
});
}
- 3-4. satırlar: seçim eşiği asenkron olarak istenir;
- satır 3: gözlemlenen süreç [metier.getSeuilElectoral(user)]'tir. Bu süreç, [subscribeOn(Schedulers.io())] G/Ç iş parçacığı üzerinde yürütülür ve UI [observeOn(SwingScheduler.getInstance()] iş parçacığı üzerinde gözlemlenir;
- 6-9. satırlar: İşlem tarafından döndürülen sonuç, grafik kullanıcı arayüzünü güncellemek için kullanılır;
- 11. satır: Olası bir istisna görüntülenir;
- 13-14. satırlar: Gözlemlenebilirin son sinyali alındığında herhangi bir işlem yapılmaz: Grafik arayüzün başlatma süreci tamamlanmıştır;
20.3.3.2. Farklı listelerin kazandığı koltuk sayısının hesaplanması
[doCalculer] yöntemi, farklı listelerin kazandığı koltuk sayısını hesaplamakla görevlidir:
@Override
protected void doCalculer() {
tListesSaisies = listesSaisies.toArray(new ListeElectorale[0]);
// koltuk sayısı hesaplaması
String info = null;
metier.calculerSieges(user, tListesSaisies).subscribeOn(Schedulers.io()).observeOn(SwingScheduler.getInstance())
.subscribe(
// sonuç işleme
result -> consumeResultSieges(result),
// istisna işleme
th -> showException(th),
// gözlemlenebilir son
() -> {
}
);
}
- 6-15. satırlar: farklı listelerin kazandığı koltuk sayısı asenkron olarak hesaplanır;
- 6. satır: Gözlemlenen işlem [metier.calculerSieges(user, tListesSaisies)]'tir. Bu işlem, [subscribeOn(Schedulers.io())] G/Ç iş parçacığı üzerinde yürütülür ve UI ile [observeOn(SwingScheduler.getInstance()] iş parçacıkları üzerinde gözlemlenir;
- 9. satır: İşlem tarafından döndürülen sonuç, [consumeResultSieges] yöntemi tarafından kullanılır;
- 11. satır: Olası bir istisna görüntülenir;
- 13-14. satırlar: gözlemlenebilirin son sinyali alındığında herhangi bir işlem yapılmaz;
9. satırda, [consumeResultSieges] yöntemi, gözlemlenen işlem tarafından döndürülen sonucu ve [sieges, elimine] alanları güncellenmiş aday listeleri kullanır:
private void consumeResultSieges(ListeElectorale[] tListesSaisies) {
// sonuç kaydediliyor
this.tListesSaisies = tListesSaisies;
// sonuçların görüntülenmesi
modèleRésultats.clear();
for (int i = 0; i < tListesSaisies.length; i++) {
modèleRésultats.addElement(tListesSaisies[i].toString());
}
// form durumunun güncellenmesi
Utilitaires.setEnabled(new JLabel[]{jLabelEnregistrer}, true);
Utilitaires.setEnabled(new JLabel[]{jLabelCalculer}, false);
Utilitaires.setEnabled(new JMenuItem[]{jMenuItemEnregistrer}, true);
Utilitaires.setEnabled(new JMenuItem[]{jMenuItemCalculer}, false);
jTextPaneMessages.setText("Calcul terminé");
}
- 4-14. satırlar: Elde edilen sonuç, grafik arayüzü güncellemek için kullanılır;
20.3.3.3. Seçim sonuçlarının kaydedilmesi
Seçim sonuçlarının kaydedilmesi, aşağıdaki [doEnregistrer] yöntemi ile gerçekleştirilir:
@Override
protected void doEnregistrer() {
// [métier] katmanına kayıt talebi
metier.recordResultats(user, tListesSaisies).subscribeOn(Schedulers.io()).observeOn(SwingScheduler.getInstance())
.subscribe(
// sonucun işlenmesi - burada sonuç yok
(param) -> {
},
// istisna işleme
(th) -> showException(th),
// gözlemlenebilir son
() -> {
// form güncellemesi
Utilitaires.setEnabled(new JLabel[]{jLabelEnregistrer}, false);
Utilitaires.setEnabled(new JMenuItem[]{jMenuItemEnregistrer}, false);
jTextPaneMessages.setText("Enregistrement des résultats réalisé");
}
);
}
- 4-17. satırlar: Seçim sonuçları eşzamansız olarak kaydedilir;
- 4. satır: Gözlemlenen işlem [metier.recordResultats(user, tListesSaisies)]'tir. Bu işlem, [subscribeOn(Schedulers.io())] G/Ç iş parçacığı üzerinde yürütülür ve UI [observeOn(SwingScheduler.getInstance()] iş parçacığı üzerinde gözlemlenir;
- 7-8. satırlar: Gözlemlenen işlem herhangi bir sonuç döndürmediğinden bu satırlar hiçbir zaman yürütülmeyecektir;
- 10. satır: Olası bir istisna görüntülenir;
- 14-16. satırlar: gözlemlenebilir nesneden son sinyali alındığında, grafik arayüz güncellenir;
Yapılması gerekenler: Swing uygulamasının çalıştığını kontrol edin. Ardından, web sunucusu / jSON ile asenkron bir işlem sırasında bir bekleme resminin ve devam eden işlemi iptal etme seçeneğinin görünmesi için grafik arayüzü ve kodu geliştirin.























