forked from burakbayramli/books
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchapdistributed.tex
2263 lines (1893 loc) · 98.4 KB
/
chapdistributed.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\movetooddpage
\chapter{Dağıtık Nesneler} \label{dist}
\thischapterhas{
\item Dağıtık nesne mimarisi
\item RMI
\item JNDI
\item EJB (Session Bean)
\item JMS
}
\versal{U}\textsc{zaktan} nesne çağırma teknolojisi, üç seviyeli (three tiered)
mimarilerin yükseldiği 90 başlarında oldukça popüler bir yaklaşım idi. Üç
katmanlı mimarilerde, orta katmanda bulunan ve network üzerinden çağırılabilen
önyüze servis eden ``nesnelere'' Uzak Metot Çağrısı (Remote Method Invocation)
ile bağlanılıyor, bu nesnelerle bilgi alışverişi yapıldıktan sonra kullanıcıya
dönülüyordu.
Fakat takip eden yıllarda Web ile yükselen Servlet odaklı yeni orta katman
mimarisi ortadaki iş mantığı katmanını ele geçirerek, kurumsal programcıların
ilgisini dağıtık nesnelerden Servlet odaklı teknolojilere çevirmesine sebep
olmuştur. Orta katmandaki iş mantığı pür Java kodları üzerinden, yâni
\PVerb!import! ile Servlet tarafından aynı JVM/süreç içine dahil edilip
çağırılabilen türden kodlar olduğu için, artık network'den çağırılabilen ve
üzerinde nesnelerin olduğu ayrı bir katmana gerek kalmıyordu. Evet bazı
mimariler bir {\em dördüncü katman} koyarak halâ Uzaktan Metot Çağrısı yapmaya
devam etmişlerdir, fakat bu mimariler hem azınlıkta, hem de optimal oldukları da
şüphe götürür bir durumda idiler.
Günümüzde zengin önyüz (rich client) teknolojilerinde hareketlenme gözükmektedir
ve zengin önyüzler, Java dünyasında Swing ile inşa edilirler. Ve, ``zengin
önyüz'' kelimesini telâfuz eder etmez orta katmanda uzak nesnelerden bahsetmemiz
gerekir çünkü zengin önyüzün HTML üreten Web orta katmanı ile konuşması mümkün
değildir. Bu bölümümüzde uzaktaki bir nesneyi network üzerinden çağırmanın
yollarını göreceğiz.
Ama ondan önce ``neden orta katman'' sorusuna cevap vermemiz gerekiyor.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Neden Orta Katman}
Orta katman, üç katmanlı bir mimaride önyüz ile veri tabanı arasında duran
seviyedir. Servlet/JSP üzerinden HTML üreten, ya da zengin önyüze servis
eden uzak nesnelerin olduğu katmanın ikisi de orta katmana iyi bir örnektir.
Eğer mimarimizde orta katman bulundurmasaydık, veri erişimini direk zengin önyüz
tarafına koymamız gerekecekti. Zengin önyüzün veri tabanına direk bağlantı
açması demek, her kullanıcının veri tabanına {\em tek bir} bağlantı açması
demektir, ve kullanıcının tüm veriye erişim istekleri bu bağlantı üzerinden
gerçekleştirilecektir.
Bu yöntemin avantajı, basit yapısıdır; Her kullanıcıya verilen bağlantı
üzerinden istenilen veri erişim ihtiyacı karşılanabilir. Dezavantajı, aynı
şekilde, her kullanıcıya bir bağlantının açılması ve o bağlantının o kullanıcıya
bağlı kalmasıdır. Her veri tabanının eşzamalı destekleyebildiği bağlantı sayısı
kısıtlıdır. O zaman her kullanıcıya bir bağlantı ayırırsak, sistemizdeki
eşzamanlı kullanıcı sayısı, veri taban bağlantı sayısı limitini hiçbir zaman
geçemeyecektir. Meselâ bir Linux makinasında kurulmuş PostgreSQL veri tabanının
optimal eşzamanlı bağlantı sayısı 200 olsun. O zaman sistemde aynı anda
çalışabilecek kullanıcı sayısı 200'dür. Bundan daha fazla olamaz.
Bu sayı, tabii ki modern yüksek ölçekli kurumsal sistemler için çok düşük bir
sayıdır. Daha fazla kullanıcıyı destekleyebilmek için modern mimarilerde veri
erişimi merkezileştirerek, taban bağlantılarını tek bir kişiye bağlamak yerine,
merkezi erişimin kontrolünde bir havuzda tutmak yolu seçilmiştir. Artık önyüzün
kendisi değil, önyüze servis eden uzak nesneler {\em ihtiyaçları olduğu anda}
bağlantıyı havuzdan alıp, işleri bitince bağlantıyı havuza hemen vereceklerdir;
Bu sayede eşzamanlı {\em taban bağlantısı} kadar {\em eşzamanlı işlem}
gerçekleştirilebilmiş olur. Zaten ölçekleme kıstasımız bu olmalıdır: Daha fazla
kullanıcıyı, daha fazla eşzamanlı işlemi karşılıyamadan destekleyemeyiz.
Her kullanıcıya tek bağlantı verdiğimiz yaklaşım niye verimsizdir? Bu yaklaşımda
kullanıcının düşünme zamanı (think time) sırasında veri taban bağlantısı hiç
kullanılmıyordu, ve bu değerli kaynağın zamanı israf edilmiş oluyordu. Yeni
yaklaşımda bağlantılar ortaktır, paylaşılabilir, ve bir kullanıcı tarafından
kullanılmadıkları zaman bir başkasının kullanabilmesi için havuzda bekliyor
olurlar. Yeni yaklaşımda, kullanıcı düşünme zamanı sırasında hiçbir zaman veri
taban bağlantısı tek kullanıcıya bağlı tutulmaz.
Orta katmanın bir diğer faydası, kodlama disiplini açısından, görsel kodları
işlem mantığından ayırmamız için hatırlatıcı bir faktör olmasıdır. Bu ayırım
tekniği, yazılım mühendisliği açısından tavsiye edilen bir yöntemdir, çünkü
görsel teknolojiyi değiştirdiğimiz zaman, işlem mantığının aynı kalabilmesini
isteriz. Özellikle birden fazla çeşit önyüz teknolojisine hizmet vermesi gereken
sistemlerde, işlem mantığının görsel kodlardan ayrı olması hayati önem
taşımaktadır. Eğer bu ayrım yapılmazsa, işlem mantığı kodu her önyüz teknolojisi
için tekrar tekrar yazılıyor olurdu (Kural \#7 ihlâli). Fiziksel olarak ayrı bir
orta katman ile çalışınca, bu katmana gidecek kodların ayrı olduğu daha bariz
olmakta, ve yararlı bir yazılım prensibini mimarimiz üzerinde dolaylı yoldan
zorlamış olmaktayız.
En son olarak Hibernate, veri tabanından okuduğu nesneleri önbellekleme
özelliğine sahip olduğu için, merkezi bir orta katmanın gerekliliği bir kez daha
oraya çıkmaktadır. Önbellek, bir kullanıcının istediği nesneyi, ikinci
kullanıcıya önbellekten servis edebildiği için, performans arttırıcı bir
faktördür, çünkü veri tabanına gitme ihtiyacını azaltır. Önbelleğin bu şekilde
kullanımı için de, iki kullanıcı da aynı merkezi yere gitmek zorundadır
(Hibernate ikinci seviye önbelleği \PVerb!SessionFactory! seviyesinde, her
JVM'de bir tane olmak üzere tutar). Bu merkezi yer de orta katmandan başkası
değildir.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section[Genel Mimari][GENEL MİMARİ]{Genel Mimari}
Uzak nesneler ile iletişim kurmanın Java dünyasında birçok değişik yöntemi
vardır. RMI, EJB (Session Bean) ve JMS üzerinden bu iletişimi gerçekleştirmenin
yollarını bu bölümde göreceğiz. Ama ondan önce, tüm bu iletişim teknolojileri
üzerinden kullanabileceğimiz, teknolojiden bağımsız bir uzak nesne mimarisi
göreceğiz. Bu mimariye, Command mimarisi adını veriyoruz.
\subsection{Command Mimarisi} \label{dist:command}
Command kalıbı ilk kez Design Patterns kitabında \cite[sf. 233]{designpatterns}
Gamma ve arkadaşları tarafından ortaya konulmuştur. Tam tanımı şöyledir:
``Command, bir isteği (request), {\em nesne} olarak tanımlayan ve bize, zengin
bir parametre listesi olarak gönderebileceğimiz, log'a yazabileceğimiz, queue
üzerinde kaydedebileceğimiz, kendi hatasını nasıl düzelteceğini (undo) bilen bir
birim ile çalışabileceğimiz bir mimari sağlar''.
Command kalıbının ilk çıkış noktasının GUI odaklı hata düzeltme işlevleri (undo)
olduğu zannedilmektedir. Bir nesnenin nesne olması için alışveriş listesi
metotları gerekiyorsa \cite[sf. 111]{ooconstruction}, Command'ın GUI için
kullanılmış olması anlaşılabilir; Hata düzeltmek, bir işlemi yapmayı bilen bir
nesne üzerinde eklenebilecek faydalı bir alışveriş listesi
(\ref{object:shoppinglist}) işlevidir. Biz bu bölümde, Command kalıbını dağıtık
nesneler mimarimizi desteklemek için kullanacağız
\cite[sf. 320]{hibernatebook}. Getireceğimiz mimarinin kuralları ve
kısıtlamaları şunlar olacak.
\begin{enumerate}
\item Önyüzden servis tarafına (orta katman) yapılan her istek, bir Command
nesnesi olmak zorundadır.
\item Her Command kurucu metotu kendisi için gereken parametreleri alıp
içinde saklamakla yükümlüdür. Olağan (default) kurucu metot çağrısı,
yanlışlıkla çağırılmaması için \PVerb!private! tanımı ile engellenecektir
(Kural \#3,\#4)
\item Command nesnelerini işletmek, tek bir işletici nesnenin sorumluluğu
olacaktır.
\item Bir Command'ın görevi, geriye bir cevap getirmek ise, bu cevap Command
nesnesinin içinde tutulacak, ve bu cevap nesnelerine \PVerb!get! erişimi
sağlanacaktır.
\item Command nesneleri, pür Java nesneleri olacak, hiçbir iletişim
teknolojisine (EJB Session Bean, JMS, RMI) bağımlı yazılmayacaklardır.
\end{enumerate}
\begin{figure}[!hbp]
\center{
\scalebox{0.31}{
\includegraphics{./images/command.eps}
}
}
\caption{\label{dist:command} Command Nesne Yapısı}
\end{figure}
\begin{figure}[!hbp]
\center{
\scalebox{0.45}{
\includegraphics{./images/command_net.eps}
}
}
\caption{\label{dist:commandnet} Network Üzerinden Command Gönderimi}
\end{figure}
\subsubsection{CommandHandler}
Command mimarisinin dağıtık mimarilere getirdiği faydaları nedir? Birincisi,
servis tarafına \PVerb!CommandHandler! üzerinden {\em tek bir giriş noktası}
sağlanmasıdır. Çok yüzlülük (polymorphism) sayesinde, üst seviye \PVerb!Command!
class'ından miras alan (inherit) alt seviye iş yapıcı Command nesneleri,
kendilerini sadece \PVerb!Command! arayüzü üzerinden gören bir
\PVerb!CommandHandler! tarafından, {\em merkezi bir yerde} işletilebiliyor hâle
gelirler (Şekil~\ref{dist:command}). Bu tek merkez, uzaktan çağrı teknolojisi
değiştirilmesi gerektiğinde tekrar yazılacak yegâne noktadır! Eğer EJB Session
Bean'den JMS'e geçiyorsanız, sadece \PVerb!CommandHandler!'ı JMS ile işleyecek
hâle getirebilirsiniz, ve bundan sonra tüm servis tarafı mimariniz JMS'te
çalışmaya başlayacaktır.
Ayrıca, bu tek giriş noktasında, tüm işlemler için gerekli olabilecek türden
``ek işlemleri'' rahatlıkla yerine getirebiliriz. Meselâ her istek sonucunda
Hibernate oturumunu kapatmak, bir Command'den gelebilecek hataları yakalayıp
Hibernate transaction'ı geri sarmak (rollback), ya da her Command için bir
işletici (worker) Thread başlatmak gibi ek işler, bu tek merkezi noktada
yapılabilir. Eğer, klasik ``her istek için ayrı bir metot'' yöntemini takip
ediyor olsaydık, tüm işlemlere lâzım olan ek işlemleri merkezi bir yerde yapmak
daha zor olurdu. Evet, Spring Framework Interceptor tekniği kullanarak her metot
için kendi istediğimiz ek kodları çengel kod takarak işletebilirdik, fakat bu
ek, teknoloji çorbasına bir teknoloji daha eklemiş olacak, Spring ayar dosyasını
fazla şişirecek, hem de, zâten paketlenmiş bir istek (Command) bekleyen JMS
seçeneği için tamamen gereksiz olacaktı.
\subsubsection{Command Nesneleri}
Command class'larının basit Java nesneleri olduğundan bahsetmiştik. Bunun
üstüne, bir Command \PVerb!Serializable! da olmalıdır, çünkü Command'ler network
üzerinden gönderilirken içerikleri bozulmadan gidebilmelidirler. Tüm
Command'lerin \PVerb!Serializable! olması için bu arayüzden en üst seviyede
miras almak gerekiyor.
\begin{lstlisting}[language=Java, frame=none]
public interface Command extends Serializable {
public void execute() throws Exception;
}
\end{lstlisting}
Her özel Command, \PVerb!Command! üst sınıfından miras aldığı için artık
otomatik olarak \PVerb!Serializable! olacaktır.
Her özel Command'ın işlem mantığı için yapması gerekenler, o Command'ın
\PVerb!execute! metotu içinde kodlanır. Command için gereken parametreler, bir
kurucu metot üzerinden verilmelidir. Geri dönmesi gereken değerler ise, Command
class'ının iç öğeleri olarak tutulmalıdırlar. Örnek olarak \PVerb!GetCarCommand!
kodlarını görelim.
\begin{lstlisting}[language=Java, frame=none]
public class GetCarCommand implements Command {
String licensePlate;
Car car;
public Car getCar() {
return car;
}
public GetCarCommand(String licensePlate) {
this.licensePlate = licensePlate;
}
public void execute() throws Exception {
Session s = HibernateSession.openSession();
HibernateSession.beginTransaction();
car = (Car)s.get(Car.class, licensePlate);
}
}
\end{lstlisting}
Hibernate transaction'ın commit edilmesi ve oturumun kapatılması işlemlerinin
Command içinde yapılmıyor olması belki dikkatinizi çekmiştir. Bu işlemler,
merkezi bir yerde (\PVerb!CommandHandler!) yapıldıkları için, ayrıca Command
içinde tekrarlanmarlına gerek yoktur.
Bu bölümün geri kalan kısmında, RMI, EJB (Session Bean) ve JMS teknolojilerini
teker teker tanıyacağız. Her teknolojiyi ilk ele aldığımızda, önce bize verdiği
dağıtık nesne özelliklerini göreceğiz. Daha sonra, Command mimarisini alıp, bu
yeni dağıtık teknoloji üzerinden çalışmasını sağlayacağız. Bu bölüm bittiğinde,
elimizde üç değişik teknolojiyle çalışabilecek bir mimari yapımız olacak. Bu
yapı öyledir ki, bir uzaktan çağırma teknolojisinden diğerine {\em projenin
ortasında bile} geçebilme şansını elde edeceksiniz. Bu tür bir esnekliğin, her
proje için faydalı olacağınız düşünüyoruz.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{RMI} \label{dist:rmi}
RMI, Uzak Nesne Çağrısı (Remote Method Invocation) Java dünyasında ilk uzak
nesne çağrı sistemidir. Kullanım açısından en basit ve servis nesneleriniz
üzerinde en az kısıt getiren teknoloji RMI'dır.
Sağladığı yetenek olarak, RMI ile uzaktaki bir nesneye network üzerinde çağrı
yapabilme özelliğine kavuşuyoruz. Önemli bir nokta, bu servis nesnesinin tekil
nesne (singleton) olacağıdır. Yâni RMI birçok bağlanan müşterinin çağrılarını,
tek bir nesneye yönlendirir. Herkesin aynı objeyi kullanacak olması, servis
tarafının çok thread'e hazır (thread-safe) olmasını gerektirir.
\begin{figure}[!hbp]
\center{
\scalebox{0.45}{
\includegraphics{./images/rmi.eps}
}
}
\caption{RMI}
\end{figure}
Servis tarafında metotları işleten Thread'lerin nasıl çalışacağı ürün
geliştiricisine bırakılmıştır. Bu sebeple projenizde Thread politikasını
kendinizin belirlemenizi tavsiye ediyoruz. Her gelen istek için yeni Thread
başlatmak, bir Thread politikası olabilir.
Bir nesneyi RMI üzerinden kullanıma açmak için, bir arayüz (interface) bir de
gerçekleştirim (implementation) kodu gerekir. Arayüz bildiğimiz Java
\PVerb!interface! kelimesi ile tanımlanan arayüzdür. Gerçekleştirim bu arayüzü
alır, ve her metotun işler kodunu tanımlar. RMI için, eski yöntemde,
\PVerb!rmic! adlı bir komut satırı programını arayüz ve gerçek kod üzerinde
işleterek, içinde network kodları taşıyan ve Stub ve Skeleton olarak bilinen iki
class üretilmesini gerektiriyordu. Biz, RMI teknolojisini Spring Framework
üzerinden kullanacağız.
\subsection{Spring}
Spring Framework, J2EE kullanımını basitleştirmeyi ve amaçlayan bir altyapı
projesidir. Spring'den bahsedilirken iki terimi sürekli duyacaksınız: Spring
{\em bir IoC kabıdır} ve Spring {\em AOP yapmanıza izin verecektir}.
\textbf{AOP}, bildiğimiz gibi, birden fazla obje, tip, metota uygulanabilecek
kod parçalarının nesnesel bir şekilde değil de çengel takar gibi koda nesne
dışından eklemlenebildiği bir programlama şeklidir. Sonradan eklenebilen bu
kodlara AOP dünyasında Aspect deniyor. Klasik örnek loglama örneğidir; Loglama,
her kodun içine direk konması yerine, AOP dünyasında bir Log Aspect'i yaratılır,
ve meselâ her metotun başında uygulanabilecek bir Aspect üzerinden dış loglama
kodunun çağırılması sağlanır. Kodunuza sonradan takılan bu çengelin işlem anında
çağrılması, AOP kabının sorumluluğudur, yâni nesnelerinizin işleyişi işlem
anında AOP sistemi tarafından izleniyor olacaktır. Hayal edebileceğiniz gibi
arka planda müthiş baytkod cambazlıkları dönmesi gerekecektir.
\textbf{IoC}, İngilizce Inversion of Control kelimelerinin kısaltılmışıdır, yâni
Kontrolün Tersine Çevirilmesi anlamına gelir. Burada anlatılmak istenen,
programcının bir nesneyi/kaynağı gidip bulması ya da \PVerb!new! ile yaratması
yerine, o nesnenin/kaynağın bir isim ile bir ayar dosyasında tanımlanması ve
Spring'in bu nesneyi o tanıma göre yaratması, sonra da bu nesneyi sizin tarif
ettiğiniz bir referans değişkenine set etmesidir. Yâni siz almıyorsunuz, size
takdim ediliyor. Kontrolün tersine çevirilmesi tanımı işte buradan
gelmektedir. Daha detaya inmek gerekirse (kod bağlamında) bahsedilen referans
değişkeni, o referansı kullanacak işlem mantığı nesnesinin içinde yer alır. Bu
referansa set eden bir metotu programcı yazmış olmalıdır, çünkü Spring, Java
Reflection kullanarak (ve ayar dosyasındaki referans ismine göre) belirlenen
set metotunu çağırır (program başında ve dinamik olarak).
Ayrıca Spring, size sadece baz anlamda AOP ve IoC özellikleri sağlayan bir paket
değil, {\em tüm J2EE servislerinin} ve gözde açık yazılım paketlerini, {\em IoC
ve AOP üzerinden kullanmanızı sağlayan} bir aracı servistir. Spring'i yazan
programcılar, hem bir IoC/AOP kabı yazmış, hem de ek olarak bu temel kabı
kullanarak J2EE arayüzleri/servisleriyle Ioc/AOP kabını teker teker bağlayarak
bir ek seviye daha ortaya çıkarmıslardır. Spring'i Spring yapan esas seviye
budur. Spring, J2EE servislerini IoC/AOP üzerinden sunmaktadır; Meselâ bir J2EE
xyz servisi size {\em takdim edilen} bir nesne olacaktır, ya da, tüm
nesnelerinize uygulanabilecek {\em J2EE Aspect'leri} olarak karşınıza
çıkacaktır.
\subsection{Basit Bir RMI/Spring Örneği}
RMI servisi için bir Spring sarmalayıcı IoC servisi vardır. Servis tarafında
\PVerb!RmiServiceExporter!, çağıran tarafında \PVerb!RmiProxyFactoryBean!
üzerinden basit Java interface'inizi, kodunuzdan tek RMI arayüzü çâğırmadan
RMI'a hazır hâle getirebilirsiniz! Servis tarafında:
\begin{lstlisting}[language=Java, caption=ServerInterface.java]
public interface ServerInterface {
public int add(int a, int b);
}
\end{lstlisting}
\begin{lstlisting}[language=Java, caption=ServerImpl.java]
public class ServerImpl implements ServerInterface {
public int add(int a, int b) {
return a + b;
}
}
\end{lstlisting}
\begin{lstlisting}[language=XML, caption=springServer.xml]
<beans>
<bean id="servis" class="org.vs.vs.ServerImpl"/>
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="serviceName"><value>AdderService</value></property>
<property name="service"><ref bean="servis"/></property>
<property name="serviceInterface">
<value>org.vs.vs.ServerInterface</value>
</property>
<property name="registryPort"><value>41199</value></property>
</bean>
</beans>
\end{lstlisting}
Görüldüğü gibi gerçek kod (implementation) \PVerb!ServerImpl! nesnesi Spring'de
\PVerb!servis! olarak isimlendirilmiş. Daha sonra bu isim, yani nesne,
\PVerb!RmiServiceExporter! adlı, \PVerb!rmic! yerine geçecek olan Spring
büyüsünü yapacak yardımcı class'a geçilmiş. Spring bu tanımları kullanarak J2EE
standartına uyumlu bir network servisi \PVerb!AdderService!'i yaratacaktır. Bu
servis objesi üzerinde gerekli tüm RMI kod üretme işlemleri yapılmış, ve
network'den kullanıma hazır hâle gelmiştir. Dikkat ederseniz, bütün bunları
yapabilmek için servis tarafı kodumuz içinde tek bir RMI API'ı çağırmamız
gerekmedi. Ayrıca, normal şartlarda \PVerb!build.xml! içinde gerekecek
\PVerb!rmic! ile yapılan Stub/Skeleton üretim işlemi de artık tamamen Spring
tarafından yapılmaktadır (koşma zamanında ama, uygulamanın başında ve bir kere
olduğu için hiçbir performans farkı hissedilmeyecektir).
Servis tarafında bu objeyi aktif hâle getirmek için, başlangıç sırasında şu
çağrıları yapabilirsiniz.
\begin{lstlisting}[language=Java, frame=none]
ClassPathXmlApplicationContext appContext =
new ClassPathXmlApplicationContext(new String[] {"springServer.xml"});
\end{lstlisting}
Çağıran tarafta ise
\begin{lstlisting}[language=XML, caption=springClient.xml]
<beans>
<bean id="remoteServer"
class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl">
<value>rmi://localhost:41199/AdderService</value>
</property>
<property name="serviceInterface">
<value>org.vs.vs.ServerInterface</value>
</property>
</bean>
</beans>
\end{lstlisting}
Burada görüldüğü gibi, uzaktaki \PVerb!ServerImpl! nesnesini çağırmak için
hiçbir RMI koduna ihtiyaç duymadık. Normâlde \PVerb!Naming.lookup! gerektiren
nesne bulmak işlemi, sadece Spring tanımları ile \PVerb!RmiProxyFactoryBean!
üzerinden yapılmaktadır. Spring'in network üzerindeki \PVerb!AdderService!'ine
erişebilecek bir nesne yaratabilmesi için, ona gereken tüm interface ve network
erişim bilgileri \PVerb!springClient.xml! tanım dosyası içinde tanımlıdır. Bu
bilgileri kullanarak servise bağlanacak kod parçası altta gösterilmiştir.
\begin{lstlisting}[language=Java, frame=none]
ClassPathXmlApplicationContext appContext =
new ClassPathXmlApplicationContext(new String[] {"springClient.xml"});
BeanFactory factory = (BeanFactory) appContext;
AdderService as = factory.getBean(``remoteServer'');
\end{lstlisting}
Ayar dosyası \PVerb!springClient.xml! içinde belirtilen port değerinin
\PVerb!springServer.xml! içindeki port değerine uyması mecburidir. Bu port'lar,
RMI kayıt (registry) port'udur. Tüm objelerin kayıt edildiği merkezi kayıt
nesnesinin servis edildiği port adresidir.
\subsection{RMI ve Command Mimarisi} \label{dist:rmi}
Şimdi, bölüm \ref{web}'de tarif edilen ve \PVerb!StrutsHibAdv! kodlarında temsil
edilen arabalar ve garajlar örneğini, RMI ve Command mimarisine
geçireceğiz. Kodların tamamlanmış hâlini \PVerb!CarsRMI! dizini altında
bulabilirsiniz.
Bu bölümün girişinde, zengin önyüzler sözkonusu olduğunda Command mimarisinin
orta katmandaki Web kodlarının yerini aldığını söylemiştik. Hem Web, hem servis
nesneleri birarada olmuyordu. Bu bölümün geri kalanında, {\em sadece örnek
amaçlı olarak}, Web kodlarımızı servis nesneleri ile konuşturacağız. Bunun
sebebi, örnek kodlarımız için Swing teknolojisine girmek istemeyişimizdir. Hem
elde mevcut olan kodları kullanmak, hem de pür Struts bazlı kodlara servis nesne
desteği ekleyince hangi noktaların değiştiğini görmek için, fiziksel mimari
açısından optimal olmayanı yapacağız. \PVerb!CarsRMI! projemiz, dört katmanlı
olacak.
Command mimarisini RMI'a geçirirken, uzaktan çağrılmaya açmamız gereken tek
class \PVerb!CommandHandler! olacak.
\subsubsection{CommandHandler}
\begin{lstlisting}[language=Java, caption=CommandHandler.java]
public interface CommandHandler {
public Command executeCommand(Command command) throws Exception;
}
\end{lstlisting}
\begin{lstlisting}[language=Java, caption=spring.xml]
<beans>
<bean id="commandHandlerImpl"
class="org.mycompany.kitapdemo.service.CommandHandlerImpl" />
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="serviceName"><value>CommandHandler</value></property>
<property name="service"><ref bean="commandHandlerImpl"/></property>
<property name="serviceInterface">
<value>org.mycompany.kitapdemo.service.CommandHandler</value>
</property>
<property name="registryPort"><value>3045</value></property>
</bean>
</beans>
\end{lstlisting}
\PVerb!CommandHandler! arayüzünü gerçekleştiren \PVerb!CommandHandlerImpl!
class'ı üzerinde, \PVerb!executeCommand! aşağıdaki gibi olacak.
\begin{lstlisting}[language=Java, caption=CommandHandlerImpl.java,numbers=left,numberstyle=\tiny]
package org.mycompany.kitapdemo.service;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
public class CommandHandlerImpl implements CommandHandler {
private Logger logger = Logger.getLogger("appLogger");
public Command executeCommand(Command command) throws Exception {
final Command param = (Command)command;
final Exception ex[] = {null};
// her yeni işlem için bir Thread aç
Thread t = new Thread (new Runnable() {
public void run() {
try {
param.execute();
HibernateSession.commitTransaction();
} catch (HibernateException exx) {
HibernateSession.rollbackTransaction();
ex[0] = exx;
} catch (Exception exx) {
HibernateSession.rollbackTransaction();
ex[0] = exx;
} finally {
HibernateSession.closeSession();
}
}
});
synchronized (this) {
t.start();
try { t.join(); } catch (Exception e) { }
if (ex[0] != null) {
throw ex[0];
}
}
return command;
}
}
\end{lstlisting}
Satır satır açıklama:
\begin{itemize}
\item \textbf{11}: Metota gelen \PVerb!command! referansı, \PVerb!final!
olarak tanımlanmış başka bir referansa transfer edilmelidir. Bunun sebebi,
biraz altta bu referansa erişecek bir iç class (inner class) olmasıdır. İç
class değişken erişim kurallarına göre, dışarıdan erişilecek tüm referanslar,
\PVerb!final! olarak tanımlanmalıdır. Eğer bu yapılmazsa, alttaki gibi bir
hata alınır:
\PVerb!``local variable command is accessed from within inner class;
needs to be declared final''!
\item \textbf{12}: Command'ı işletirken ortaya çıkabilecek
\PVerb!Exception!'ları iç class'ın dışına taşıyabilmek için, bir
\PVerb!Exception! {\em dizini} tanımlıyoruz. Niye sadece basit bir
\PVerb!Exception! referansı değil? Çünkü, iç class içinden, dışarıdaki bir
referansa erişmemiz gerekirse bu referans \PVerb!final! olmalıdır. Final olan
bir referansa \PVerb!=! ile hiçbir şey set edemeyiz! Ama bu engele tıkanıp
kalmak yerine, bir hack ile işi çözüyoruz: İç class'tan bir referansa set
edemesek te, \PVerb!final! olan bir dizin {\em içine} atama yapmak, hâla legal
bir harekettir. Biz de \PVerb!Exception! yerine \PVerb!Exception[]!
kullanarak problemi çözüyoruz.
\item \textbf{15,16}: \PVerb!Runnable! arayüzünü gerçekleştiren bir class'ı
anında oluşturup, anında Thread nesnesine çalıstırılmaya hazır olarak
veriyoruz. Bu kullanım, biraz normâl dışı bir kullanımdır, fakat yeni bir
Thread tarafından işletilecek kodları aynı kod sayfasında görebilmek için çok
güzel bir tekniktir. Alternatif olarak, aynı kod bloğunu ayrı bir dosyaya ve
class'a koyup ve Thread'e bu class'ı verebilirdik, ama bu, kod idaresi için pek
faydalı olmazdı.
\item \textbf{17-20}: İlk önce Command işletilir, ve işler yolunda gitti ise
(hiç Exception atılmamışsa) transaction commit edilir.
\item \textbf{20-23}: Hibernate tarafından atılan bir hata var ise, burada
yakalanır. Bu durumda, transaction geriye sarılmalı (rollback) ve ele geçen
hata \PVerb!ex! dizini üzerine yazılmalıdır.
\item \textbf{23-26}: Burada Hibernate hatası değil, daha genel bir hata
olmuştur. Yapılan hata karşılama hareketleri yine aynıdır, burada ayrı bir
bölüm olmasının sebebi, ileride değişik hata karşılama işlemlerini burada
yapılabilecek olmasıdır. En azından her \PVerb!catch! kendi log ifadelerini
yazarak, ne tür bir hata meydana çıktığını servis tarafı log'larında
gösterebilir.
\item \textbf{26-28}: Hata olsa da olmasa da işleyecek \PVerb!finally! bloğu
içinde Hibernate oturumu kapatılır.
\item \textbf{39}: Command işletildikten sonra {\em geri döndürülür}. Bu çok
önemlidir çünkü geriye sonuç döndürmesi gereken Command'ler, sonuç değerlerini
yine kendi üzerlerinde saklayacakları için, aynı Command'in geri dönmesi çok
önemlidir. Çağıran tarafta istediği değerlere erişmek isteyenler, Command
üzerinde \PVerb!get! çağrıları yaparak servis tarafından gelen sonuçlara
erişebilirler.
\end{itemize}
\subsubsection{Çağıran Taraf}
Çağıran (Web) tarafında Spring tanımı alttaki gibi olacaktır.
\begin{lstlisting}[language=Java, frame=none]
<beans>
<bean id="commandHandler"
class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl">
<value>rmi://localhost:3045/CommandHandler</value>
</property>
<property name="serviceInterface">
<value>org.mycompany.kitapdemo.service.CommandHandler</value>
</property>
</bean>
</beans>
\end{lstlisting}
Bu tanım sayesinde, \PVerb!getBean! kullanarak gereken her yerde
\PVerb!CommandHandler! referansını alabiliriz. Eğer bu referansın alımını
hızlandırmak istiyorsak, Struts/Web ortamında, bu referansı oturum üzerinde
sürekli hazır tutabiliriz, ve gerekince \PVerb!getAttribute! ile oradan alırız.
\PVerb!CommandHandler! Referansını hazırlamak ve set etmek için bir Servlet
filtresi yazmamız gerekiyor. Bu filtre, her Web isteği başında ``oturum üzerinde
bir \PVerb!CommandHandler! referansı olup olmadığını'' kontrol eder. Eğer yoksa,
\PVerb!factory.getBean! ile bir tane alır ve oturum üzerine koyar.
\begin{lstlisting}[language=Java, caption=ServiceReferencesFilter.java]
public class ServiceReferencesFilter implements Filter {
private Logger logger = Logger.getLogger("appLogger");
public void init(FilterConfig filterConfig) throws ServletException { }
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException,
ServletException
{
HttpSession session = ((HttpServletRequest) request).getSession();
if (session.getAttribute("commandHandler") == null) {
CommandHandler commandHandler =
(CommandHandler)AppStartup.factory.getBean("commandHandler");
session.setAttribute("commandHandler", commandHandler);
}
chain.doFilter(request, response);
}
public void destroy() { }
}
\end{lstlisting}
Artık her Struts Action'i içinden \PVerb!ComnmandHandler! referansına
erişebiliriz. Struts Action, servis ile iletişime geçmesi gerekince, gereken
Command'i \PVerb!new! ile yaratır, \PVerb!CommandHandler!'a network üzerinde
gönderir, ve Command'ın işletilmesini sağlar.
\begin{lstlisting}[language=Java, caption=ShowCarDetailAction.java]
public class ShowCarDetailAction extends Action
{
private static Logger logger = Logger.getLogger("appLogger");
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
CommandHandler handler =
(CommandHandler)request.getSession().getAttribute("commandHandler");
String licensePlate = request.getParameter("licensePlate");
GetCarCommand cmdGet = new GetCarCommand(licensePlate);
Command resGet = handler.executeCommand(cmdGet);
Car car = ((GetCarCommand)resGet).getCar();
DynaActionForm daf = (DynaActionForm) form;
BeanUtils.copyProperties(daf, car);
if (car.getGarage() != null &&
!car.getGarage().equals(new Integer(0)))
{
daf.set("garageId", car.getGarage().getGarageId());
}
request.getSession().setAttribute("car", car);
// ....
return mapping.findForward("success");
}
}
\end{lstlisting}
Car nesnesi \PVerb!setAttribute! ile oturum üzerine konduktan sonra, JSP sayfası
arabanın detaylarını gösterecektir.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{JNDI}
Birazdan işleyeceğimiz EJB ve JMS teknolojilerini kullanmak için, JDNI adlı bir
teknolojiye ihtiyacımız var. JNDI'ı biraz daha yakından tanıyalım.
JNDI (Java Naming and Directory Interface) arayüzleri, değişik türden dizin
(directory) ve isimlendirme (naming) servislerine standart bir arayüz sağlar
\cite[sf. 24]{jmsbook}. Bu açıdan JNDI, JDBC'ye benzer. Aynen JDBC'nin değişik
türden veri tabanları ile konuşmamızı sağladığı gibi, JNDI da dizin ve
isimlendirme servisleri olan LDAP, Novel Netware NDS, CORBA Naming Service ve
şirketlere özel (proprietary) isimlendirme servislerine erişmemizi yardımcı
olur. JBoss içinde de JNDI standart arayüzleri üzerinden erişebileceğimiz
JBoss'a özel bir dizin ve isimledirme servisi vardır.
JNDI üzerinden birçok değişik servise erişebilirsiniz. Bu servisler, bir JMS
Queue'su, bir Session Bean'i, ya da fiziksel bir yazıcı bile olabilir. Yâni
JNDI, bir servis ile bir ismi birbirine ilintilendirerek, o servise o isim
üzerinden erişebilmenizi sağlar.
RMI bölümünde, uzaktaki bir nesneye erişmek için JNDI'a konusuna girmemiz
gerekmedi, çünkü hem servis hem bağlanan tarafı Spring üzerinden
hallediyorduk. Spring, JDNI işlemlerini kapalı kapılar arkasında kendisi
hallettiği için bizim fazladan JNDI işlemi yapmamıza gerek kalmamıştı. Fakat,
görmek üzere olduğumuz EJB ve JMS teknolojileri için JNDI kullanacağız, çünkü
JMS ve EJB teknolojilerini pür hâlde kullanıyoruz (böylesi daha kolay
olacak). EJB bağlamında JDNI'ı, bir Session Bean'i ismiyle bulmak için, JMS için
ise bir Queue'ya ismini kullanarak erişebilmek için kullanacağız.
JBoss için standart JNDI kodlama kalıbımız şöyle olacak:
\begin{lstlisting}[language=Java, frame=none]
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;
import javax.naming.Context;
import java.util.Properties;
import java.util.List;
...
InitialContext ic = null;
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES, "jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "localhost:1099"); // JNDI port 1099 ise
try {
ic = new InitialContext(p);
Object objref = ic.lookup("buraya/jndi/ismi/yazilir");
// ...
// burada elde edilen referans ile işlemler yapılabilir
} catch (Exception e) {
// hata durumunu rapor et
e.printStackTrace();
}
\end{lstlisting}
Örnekte kullandığımız JNDI port'u, \PVerb!1099! numaralı port'tur. Fakat bu port
değeri bazı JBoss kuruluşlarında değişik olabilir (paketten çıkan hâli, \PVerb!1099!
olmak üzere ayarlıdır). Gerçek JNDI port'unuz değişik ise, bu değişik değerin ne
olduğunu anlamak için JBoss açılırken basılan log mesajlarına bakmanız yeterli
olacaktır. Örnek bir JBoss log ekranı aşağıda gösterilmiştir.
\begin{lstlisting}[language=XML, frame=none]
16:53:57,287 INFO [Server] Starting General Purpose Architecture (GPA).
16:53:58,539 INFO [ServerInfo] Java version: 1.4.2,Sun Microsystems Inc
16:53:58,549 INFO [ServerInfo] Java VM: Java HotSpot(TM) Client VM 1.4.
n Microsystems Inc.
16:53:58,549 INFO [ServerInfo] OS-System: Windows XP 5.1,x86
16:53:59,250 INFO [Server] Core system initialized
16:54:02,395 INFO [WebService] Using RMI server codebase: http://bio:80
16:54:03,126 INFO [NamingService] Started jndi bootstrap jnpPort=1099,
1098, backlog=50, bindAddress=/0.0.0.0, Client SocketFactory=null, Serve
Factory=org.jboss.net.sockets.DefaultSocketFactory@ad093076
16:54:11,338 INFO [Embedded] Catalina naming disabled
16:54:12,710 INFO [Http11Protocol] Initializing Coyote HTTP/1.1 on http
-8080
\end{lstlisting}
Bu mesaj satırları içinde \PVerb!jnpPort! ibaresinin verildiği satıra
bakınız. Eşitliğin sağındaki değer, JBoss'un JNDI servisi için kullandığı port
değeridir.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{EJB Session Bean} \label{dist:ejb}
EJB teknolojisi, üç değişik kısımdan meydana gelir.
\begin{itemize}
\item Konumlu (Stateful) Session Bean (SFSB)
\item Konumsuz (Stateless) Session Bean (SLSB)
\item Entity Bean
\end{itemize}
Bu seçeneklerden Entity Bean teknolojisi, EJB'nin programcılar tarafından en
tepki çeken ve kabul görmeyen kısmı olmuştur. Kalıcılık (persistence) problemini
çözmeye çalışan Entity Bean'ler, çözdüklerinden daha fazla problemi
beraberlerinde getirmişlerdir. Entity Bean teknolojisi, Kural \#1, \#5 ve
\#6'in çok ciddi şekilde ihlâlleridir.
Fakat, SFSB ve SLSB bileşenleri dağıtık mimarilere faydalı
olabilirler. Özellikle, JBoss Uygulama Servisi'nin Session Bean'lere sağladığı
çökmeden kurtulma (failover), yük dağıtımı (load balancing) servisleri
sayesinde, sağlam ve ölçeklenebilen Session Bean bazlı sistemler kurmak
mümkündür.
\subsection{Sonuç Dizin Yapısı}
JBoss üzerinde bir EJB'yi işletmek için \PVerb!deploy! dizininde kurulması
gereken dizin yapısı altta gösterilmiştir.
\begin{lstlisting}[language=Java, frame=none]
+- kitapdemo.ear
| +- META-INF
| | +- MANIFEST.MF
| | +- application.xml
| | +- jboss-app.xml
| +- conf
| | +- log4j.xml
| +- kitapdemo.sar
| | +- META-INF
| | | +- MANIFEST.MF
| | | +- jboss-service.xml
| | +- conf
| | +- activation.jar
| | ..
| | +- xml-apis.jar
| +- hibernate.cfg.xml
| +- kitapdemo.jar
| | +- META-INF
| | | +- ejb-jar.xml
| | | +- jboss.xml
| | | +- MANIFEST.MF
| | +- org
| | | +- mycompany
| | | | +- kitapdemo
| | | | | +- dao
| | | | | +- pojo
| | | | | | +- Car.class
| | | | | | +- Car.hbm.xml
| | | | | | +- Garage.class
| | | | | | +- Garage.hbm.xml
| | | | +- service
| | | | +- util
\end{lstlisting}
Bu yapı, örnek projelerin \PVerb!build.xml!'i tarafından otomatik olarak
kurulacaktır.
\subsection{SFSB ve SLSB}
Eğer uzaktan çağrı yaptığımız Session Bean bir SFSB ise, o Bean'e elimizde bir
referans olduğu sürece, o Bean muhafaza edilir. SFSB'lerde, aynen \PVerb!new!
ile yaratılan Java nesnelerindeki gibi, bir önceki yapılan çağrı bir sonrakini
etkiler çünkü eldeki SFSB, JBoss tarafından sabit/aynı tutulur. Kıyasla eğer
eldeki referans bir S\textbf{L}SB'e işaret ediyor olsaydı, JBoss Uygulama
Servisi konumsuz olarak bildiği bu nesneyi iki çağrı arasında değiştirebilirdi.
Peki Uygulama Servisi bu SLSB değiştirmesini niye yapar? Genellikle elde bir
SLSB havuzu vardır ve gelen her yeni istek için havuzdan bir nesne alınıp çağrı
üzerinde yapılır ve nesne havuza geri konulur. Önceden yaratılmış ve havuzdan
servis edilen nesnelerin tekrar bir \PVerb!new! aşamasından geçmesi gerekmez, ve
hazır olan nesnelerin servis edilmesinin daha hızlı olduğu savunulduğu için bu
yapı seçilmiştir. Havuzdan alma, havuza geri koyma işlemi, her yeni çağrıda
yapılabilir, çünkü nesneler konumsuzdur; Bir çağrı, bir sonrakini etkilemez.
Tabii EJB belirtimleri (specification) aslında havuzlamadan bahsetmiyor;
Belirtim sadece, SLSB'ler çağırılırken aynı nesnenin orada olacağına
güvenilmemesinden bahsetmektedir (SFSB için ise tam tersi geçerlidir). Havuz
kullanıp kullanmama gibi detaylar, her ticari uygulama paketinin kendi seçimine
bırakılmıştır.
\subsection{SFSB}
Kodlama açısından bir SFSB için, üç tane class yazılması gerekiyor. Bunlar Home,
Remote arayüzleri, ve gerçekleştirim kodlarıdır. Bu bölümde örnek olarak
\PVerb!MyTestSession! adında bir SFSB yazacağız. Bu Bean üzerinde
\PVerb!increment! ve \PVerb!getCount! adlı iki metot olacak. \PVerb!Increment!
metotunu kullanarak SFSB üzerindeki bir sayaç değeri arttırabileceğiz,
\PVerb!getCount! ile mevcut sayının okunması mümkün olacak. Bu örneğin çok uygun
bir Konumlu (stateful) Session Bean örneği olduğunu herhalde anlamışsınızdır,
eğer \PVerb!MyTestSession! konumlu değil konumsuz bir Bean olsaydı,
arttırdığımız değerlerin hatırlanması mümkün olmazdı. Zaten SFSB ve SLSB
arasındaki farkı anlamak için bu değiştirmeyi göstereceğiz.
Kod, alttaki gibi olacak (bitmiş kodları ve Ant \PVerb!build.xml! dosyasını
\PVerb!CounterStateful! projesi altında bulabilirsiniz):
\begin{lstlisting}[language=Java, frame=none]
public interface MyTestSession extends javax.ejb.EJBObject{
public void increment() throws java.rmi.RemoteException;
public int getCount() throws java.rmi.RemoteException;
}
public interface MyTestSessionHome extends javax.ejb.EJBHome
{
public MyTestSession create() throws
javax.ejb.CreateException,
java.rmi.RemoteException;
}
public class MyTestSessionBean implements SessionBean
{
int counter = 0;
public void ejbCreate() throws CreateException { }
public void setSessionContext( SessionContext aContext )
throws EJBException {}
public void ejbActivate() throws EJBException { }
public void ejbPassivate() throws EJBException { }
public void ejbRemove() throws EJBException { }
public void increment(){ counter++; }
public int getCount(){ System.out.println(counter); return counter; }
}
\end{lstlisting}
Bu kodların deploy edilmesi için \PVerb!ejb-jar.xml!, \PVerb!jboss.xml!,
\PVerb!application.xml!, \PVerb!jboss-app.xml! ve \PVerb!jboss-service.xml!
adında beş dosya gerekmektedir. Tüm bu dosyalara teker teker bakalım
(\PVerb!jboss-service.xml!'i daha önce \ref{web:config:jbosservice} bölümünde
işlemiştik).
\begin{lstlisting}[language=XML, caption=jboss-app.xml]
<jboss-app>
<module>
<service>kitapdemo.sar</service>
</module>
</jboss-app>
\end{lstlisting}
\begin{lstlisting}[language=XML, caption=application.xml]
<application>
<display-name>KitapDemo</display-name>
<description>KitapDemo Queue</description>
<module>
<ejb>kitapdemo.jar</ejb>
</module>
</application>
\end{lstlisting}
\begin{lstlisting}[language=XML, caption=ejb-jar.xml]
<ejb-jar>
<description>Kitapdemo</description>
<display-name>Kitapdemo</display-name>
<enterprise-beans>
<!-- Session Beans -->
<session>
<display-name>My Test Session Bean</display-name>
<ejb-name>test/MyTestSession</ejb-name>
<home>org.mycompany.kitapdemo.service.MyTestSessionHome</home>
<remote>org.mycompany.kitapdemo.service.MyTestSession</remote>
<ejb-class>
org.mycompany.kitapdemo.service.MyTestSessionBean
</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
<assembly-descriptor>
</assembly-descriptor>
</ejb-jar>
\end{lstlisting}
\begin{lstlisting}[language=Java, caption=jboss.xml]
<jboss>
<enterprise-beans>
<session>
<ejb-name>test/MyTestSession</ejb-name>
<jndi-name>ejb/test/MyTestSessionBean</jndi-name>
</session>
</enterprise-beans>
<resource-managers>
</resource-managers>
</jboss>
\end{lstlisting}
Görüldüğü gibi \PVerb!application.xml! ve \PVerb!jboss-app.xml! oldukça
basmakalıp dosyalardır. Her proje için bir kez yaratılırlar, ve hiçbir EJB'ye
has bir tanım içermezler. Bu iki dosyanın amacı, EJB kodlarının bulunduğu JAR ve
SAR dosyalarının isimlerini EAR paketine tanıtmaktır.
Her EJB'ye özel ayarların yapıldığı yer \PVerb!ejb-jar.xml! dosyasıdır. Meselâ
eğer EJB'nin konumsuz olmasını istersek, yukarıdaki \PVerb!<session-type>!
etiketi içinde \PVerb!Stateful! yerine \PVerb!Stateless! kelimesini
kullanabilirdik.
EJB tanımlaması hazırsa, \PVerb!ejb-jar.xml! içindeki Session Bean tanımını bir
JNDI ismine bağlayan yer de \PVerb!jboss.xml! dosyası olacaktır. Bu dosyaya
göre, EJB'mizin JNDI üzerinden bulunabileceği isim
\PVerb!ejb/test/MyTestSessionBean! ismi olarak belirtilmiştir.
Şimdi JNDI üzerinden \PVerb!MyTestSessionBean! EJB'sini bulan ve çağrı yapan
bağlantı kodunu görelim.
\begin{lstlisting}[language=Java, caption=Mainline.java]
public class Mainline {
public static void main(String[] args) {
MyTestSession beanRemote;
InitialContext ic = null;
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES, "jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "localhost:1099"); // JNDI port.
try {
ic = new InitialContext(p);
Object objref = ic.lookup("ejb/test/MyTestSessionBean");
MyTestSessionHome testSessionBean = (MyTestSessionHome)
PortableRemoteObject.narrow(objref, MyTestSessionHome.class);
beanRemote = testSessionBean.create();
while (true) {
beanRemote.increment();
System.out.println("Count is : " + beanRemote.getCount());
try {
Thread.currentThread().sleep(1000); // uykuya yat
} catch (Exception e) { }
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Mainline() { }
}
\end{lstlisting}
Test kodumuzda önce JNDI üzerinden \PVerb!MyTestSession! bulunuyor, daha sonra
bu referans üzerinden, sonsuz bir döngü içinde SFSB üzerindeki sayaç bir
arttırılıp, yeni değer ekrana basılıyor. SFSB içindeki servis kodunu
hatırlarsanız, ekrana basma işlemi hem servis hem de çağıran tarafında
yapılmaktadır. \PVerb!Narrow! ve \PVerb!create! çağrıları oldukça basmakalıp
çağrılar oldukları için detaylarına inmeyeceğiz. Her EJB için ezbere takip
edilmesi gereken metotlardır.
\subsubsection{Çağıran Taraf ve Import}