Java daki Problemler ve OSGi Teknolojisinin Getirdiği Çözümler
Mehmet Mustafa GÜRSUL
Uzman Yazılım Mühendisi
İÇERİK
Özet 1
1.Modülerlik 1
2.Java JAR Dosyaları ve Problemleri 3
3.Java EE Sınıf Yükleme Mekanizması 8
4.OSGi Teknolojisi 10
5.The OSGi Alliance and Standards 12
6.OSGi Implementations 13
7.OSGi Alternatifleri 13
8.Sonuç ve Değerlendirme 14
Kaynaklar 14
Özet
Bu dokümanda Java da dinamik modül sistemini gerçek anlamda oluşturmaya çalışan OSGi dan bahsedilmektedir.
OSGi kavramını tam anlamak için ilk önce;
-
Modülerliğin ne demek olduğunu?
-
Java da bizlere nelerin nasıl sorun olduğunu anlatılarak OSGi ın getirdiği çözümler daha iyi anlaşılır hale getirilmektedir.
Anahtar Kelimeler: OSGi, Java, Jar
Modülerlik
Herhangi bir büyük bir mühendislik projesinde, örneğin yeni bir köprü, gökdelen, uçak tasarımında, aşılması gereken en önemli konu nedir acaba? Cevap herhalde karmaşıklık (complexity) olur.
Örneğin Boeing 747-400 diğer bir adıyla “Jumbo Jet” de;
-
6 milyon parça bulunmaktadır.
-
171 mil (274 km) lik kablo
-
Orijinal tasarımı için 75000 mühendislik çizimi kullanıldı.
Çok çok karmaşık bir makina. Çok karmaşık olduğundan bir kişi Boeing 747 nin nasıl çalıştığını tam olarak her şeyiyle bilemez. Aynı zamanda ilk olarak 1960 yılında tasarlanmıştır ve şu anki modern hali defalarca gelişerek karmaşıklık seviyesini daha da artırmıştır.
İnsanın bu karmaşık makineyi başarıyla tasarlayabilmesinin tek bir yolu vardır. O da bu makineyi küçük parçalara ayırarak daha anlaşılabilir modüler halde tasarlamasıdır.
Modülerlik birkaç önemli kazanç sağlamaktadır.
-
İş Bölümü (Division of labour) :
-
Kişilere ya da gruplara; ayrılmış modüller üzerinde çalışacakları işler atanabiliyor.
-
Kişilerin sadece kendi modülleri hakkında bilgi sahibi olma gereksinimi var diğer modüller üzerinde bilgi sahibi olmaları gereksinimi yok.
-
Örneğin uçaktaki eğlence sistemini kuracak kişiler uçağın iniş sisteminin nasıl olacağı ile ilgili bilgi sahibi olmalarına gerek yoktur. Eğlence sisteminin en iyi nasıl kurulacağı üzerine yoğunlaşırlar. Tersi de geçerli tabiki.
-
Soyutlama (Abstraction) :
-
Uçağı bir bütün halinde soyut bir kavram gibi düşünelim, her bir parçasının ne işe yaradığını tam anlama ihtiyacımız yoktur.
-
Örneğin uçağın uçması için kanatların kalkmasını ve uçağın ileriye doğru itme kuvvetinin olmasını kontrol etmeliyiz. Burada motorlara yakıtın nasıl aktarıldığını ve kanatların açılıp kapanması için kaç tane motora ihtiyacımız olduğunu bilmemize gerek yoktur.
-
Yeniden Kullanılabilirlik (Reuse) :
-
Uçağın belki de küçük bileşenlerine bile çok büyük emek harcadıktan sonra başka bir uçak tasarımında aynı ya da benzer bir bileşene ihtiyaç duyduğumuzda sıfırdan yeniden başlayarak tasarım yapmak herhalde istenmeyen bir durum olur.
-
Yeni uçakta; minimum değişikliklerle yeniden kullanılabilir bileşenler kullandığımızda işimiz kolaylaşacaktır.
-
Kolay Bakım ve Onarım (Ease of Maintenance and Repair) :
-
Uçağın bir tekerleğinin patlaması sonrası ya da oturulacak yerlerin kaplamasının yırtılması sonrası tüm uçağı ıskartaya çıkarmak herhalde istenmeyen bir durum olur.
-
Modüler tasarımla çalışmayan modülün çıkarılması, onarılması ya da yerine yeni modülün konulmasını, bunları yaparken de diğer kalan sistemin etkilememesi amaçlanır.
Tabi ki burada bir yazılım ürününün bir mühendislik disiplini içerisinde ele alınıp alınmayacağı tartışılabilir. Fakat bununla birlikte yazılımın karmaşıklığı diğer alanlarla yarışabilecek düzeydedir.
Modern yazılımlar karmaşık seviyede aynı zamanda bu karmaşıklık seviyeside gün geçtikçe artmaktadır.
Örneğin NASA’nın eski uzay mekiğindeki bilgisayarda yaklaşık 500.000 satırlık kod varken şimdi evimizdeki DVD oynatıcısında bile yaklaşık 1 milyon satırlık kod bulunmaktadır.Windows XP tahmini olarak 40 milyon, Vista da 50 milyon satır kod barındırmaktadır.
Sonuç olarak; Yazılım geliştiricileri de aynen uçak mühendisleri gibi büyük ve karmaşık işler yapmaktadır.
Java Programlama dili, hem büyük hem de karmaşık kurumsal yazılımlar ile küçük ama çok yaygın olan mobil uygulamalar yapabilmek için de kullanılan popüler dillerden birisidir.
Bununla birlikte Java modülerliği tam anlamıyla desteklememektedir.
Birazdan var olan Java mekanizmasının, biraz önce değinilen modülerlik ile ilgili 4 maddeyi gerçekleştirmede neden başarısız olduğunu görülecektir.
Bu arada Java nın en önemli gücü esnek olmasıdır. Bu sayede modül sistemini kolayca Java nın üzerine inşa edebilmekteyiz. Bu modül sistemi OSGi olarak adlandırılmakta.
Modül Ne Demektir?
Yazılımda modül neye benzer olmalıdır? Aşağıdaki özelliklere sahip olmalıdır.
-
Self-Contained : Modül bir bütün olarak mantıksaldır. Tek bağımsız bir birim halinde install, uninstall edilebilir. Atom değildir yani daha küçük parçalardan oluşmuştur, fakat bu parçalar kendi başlarına olamazlar. Eğer herhangi bir parçası kaldırılırsa da modülün işlevselliği sona erebilir.
-
Highly-Cohesive : Cohesion, modülün sorumlulukları ile ne kadar ilgili ya da odaklandığı ile ölçülür. Modül ilgisiz şeyler yapmamalıdır. Bir tane mantıksal amaca bağlı kalmalı ve bu amacı tam olarak yapmaya odaklanmalıdır.
-
Loosely Coupled : Modül etkileşimde olduğu diğer modüllerin gerçekleştirim (implementation) detaylarına bağımlı olmamalıdır. Loose Coupling bizlere şu imkanı vermektedir: Bir modülün gerçekleştirimini değiştiririz. Fakat bunu yaparken bu modülü kullanan diğer modüllerde herhangi bir güncelleme gereksinimine ihtiyaç duymayız.
Bu üç özelliği de modülde sağlamak için modülün çok iyi tanımlanmış arayüzlere ihtiyacı vardır.
Kararlı arayüzler;
-
Modüller arasında mantıksal sınırlamaları sağlamakta
-
İç gerçekleştirim detaylarına erişimi engellemektedir.
İdeal olarak arayüzler;
-
Her bir modülün diğer modüllere neleri sunduğunu ,
-
Ve her bir modülün de diğer modüllerden nelere gereksinimi olduğunu
belirtir biçimde olmalıdır.
Java JAR Dosyaları ve Problemleri
Java da standart deployment birimi JAR File Specification a göre belirlenmiş olan JAR dosyalarıdır.
Birçok dosyayı bir tek dosyada toplayan Zip dosyası formatındadır. Genel olarak derlenmiş Java ve kaynak dosyaları (images, dokümantasyon v.b.) içerir. Ek olarak META-INF dizininde JAR ile ilgili bilgi içeren dosyalar bulunmaktadır. Bunlardan en önemlisi MANIFEST.MF dosyasıdır.
Çok nadiren bir uygulama bir tek JAR dosyasından oluşur. Genellikle bir uygulama birçok JAR dosyasının bir araya gelmesiyle oluşur.
JAR dosyaları kullanıldığında karşılaşılan 4 büyük problem şunlardır:
-
Çalışma zamanında (runtime da) JAR anlamlı bir kavram değil, sadece build zamanı ve deploy zamanında anlamlı. JVM çalıştığında tüm JAR dosyalarının içindeki dosyalar birleştirilerek bir tek liste oluşturulur ve classpath e eklenir.
-
Bağımlılıklarını belirten standart bir meta-data sı içermemektedir.
-
Versiyonlanmamıştır ve aynı anda JAR ın birden çok versiyonu yüklenememektedir.
-
JAR lar arasında information-hiding mekanizması yoktur.
Sınıf Yükleme ve Global Classpath
-
“classpath”
-
“java” komutuna parametre olarak geçmektedir.
-
JAR dosyalarının listesini ve derlenmiş java dosyalarının dizinini belirtmektedir.
-
Örneğin aşağıdaki komut java uygulamasını classpath inde log4j.jar ve classes dizinini içerecek biçimde başlatmakta. Son parametre çalıştırılacak main metodu bulunan java sınıfı. classes dizini içerisinde org/example/HelloWorld.class şeklinde içerisinde yer almaktadır.
java -classpath log4j.jar:classes org.example.HelloWorld
Not: UNIX için parametre ayrımı “:” ile Windows için “;” iledir.
-
JVM bir şekilde .class uzantısındaki dosyaları fiziksel diskte bulup Class objesine çevirdikten sonra belleğe yükleyip static main metodunu çalıştırması gerekiyor. Bu standart JRE de nasıl oluyor ona bakalım :
-
Java da sınıfları yükleyen sınıf java.lang.ClassLoader sınıfıdır ve 2 tane sorumluluğu vardır.
-
Mantıksal isimden .class dosyasının fiziksel ortamda bulunması
-
Fiziksel ortamdaki bu byteların Class nesnesine bellekte çevrilmesi.
-
ClassLoader sınıfını ektend edip kendi gerçekleştirimimizi de yapabiliriz ayrıca. Böylece sınıfları bir network ten ya da dosya tabanlı olmayan bir sistemden bulup yükleyebiliriz. Ama ikinci kısım olan sınıftaki fiziksel byteların Class nesnesine çevrim işi ClassLoader daki native ve final olan yani yeniden implement edilemeyen (not overriden) bir method olan defineClass() ile olmaktadır.
-
java -classpath log4j.jar:classes org.example.HelloWorld
bu komutu çalıştırdığımızda JRE org.example.HelloWorld sınıfını yüklemesi gerektiğini anlar. Özel bir ClassLoader olan Application Class Loader a yüklemesini söyler. Application Class Loader da bir üst sınıf olan Extension ClassLoader a, o da bir üst sınıfı olana Bootstrap ClassLoader söyler. Default olarak her classloader ilk önce bir üst sınıfına sorar. Eğer üst sınıfı bulmaz ise kendi arayıp yüklemeye çalışır.
HelloWorld örneğimize dönecek olursak;
JRE base de ve extension library de bulunmadığını kabul ediyoruz. Bundan dolayı bootstrap ve extension class loader lar tarafından bulunamayacaktır ve application class loader bulmaya çalışacaktır. Bunu yaparken
-
Classpath deki sırasıyla herbir entry de bakacak ve bulur bulmaz da aramayı durduracaktır.
-
Classpath deki herbir entriye bakıp bulamazsa tanıdık bir exception olan ClassNotFoundException ı verecektir.
Özet olarak;
-
JRE Application ClassLoaderdan sınıfı yüklemesini ister
-
Application ClassLoader , Extension ClassLoader dan sınıfı yüklemesini ister
-
Extension ClassLoader, Bootstrap ClassLoader dan sınıfın yüklenmesini ister
-
Bootstrap ClassLoader sınıfı bulamaz, Extension Class Loader bulmaya çalışır
-
Extension ClassLoader sınıfı bulamaz, Application ClassLoader classpath deki log4j.jar dan aramaya başlar
-
Sınıf log4j.jar da bulunamayınca classes dizininde aranmaya başlar
-
HelloWorld sınıfı bulunur ve yüklenir.
Her bir sınıf istemi için tekrar 1. Adımdan başlayarak arama yapılır.
Sınıfların Çakışması
Java da sınıf yüklemeleri çoğu zaman doğru çalışır fakat aşağıdaki durum olduğunda neler olacağını düşünelim.
Diyelim ki yanlışıkla yardim.jar adında HelloWorld ün eski bir versiyonunu içeren bir jar ı classpathe ekledik.
java -classpath yardim.jar:log4j.jar:classes org.example.HelloWorld
yardim.jar classes dan once geldiğinden application class loader yardim.jar içinde HelloWorld ü bulunca aramayı durduracaktır ve bu komutla her zaman HelloWorld ün eski versiyonu yüklenecektir. Ve bizim classes dizinimizdeki HelloWorld hiç bir zaman kullanılmayacaktır. Bu durumda da sınıf tam olarak beklenen işlemleri yapamayacak ya da yaptığımız değişikliklerin hiç bir etkisi olmayacaktır.
Bu durum Java daki önemli sorunlardan birisidir.
Bu senaryo pek olmayacak bir senaryo gibi gelebilir ya da sıradan basit uygulamalarda bu durum oluşabilir ve biz de sorunu hemen bulup düzeltebiliriz diyebiliriz. Fakat büyük çaplı Java uygulamaları onlarca bazen de yüzlerce jar dosyasından oluşmaktadır ve bunların hepsi de classpath de gözükmek zorundadır. Ayrıca JAR dosyaları bazen içeriği ile tam da anlamlı bir isme sahip olmayabiliyor. Bu durumda aynı isme sahip çakışan sınıflar olma ihtimali olmaktadır. Örneğin en çok karşılaşılan sorun aynı Jar ların farklı versiyonlarındaki sınıflarının çakışmasıdır.
Broken internal jar dependecy
JRE tum jarları bir tek liste gibi gördüğünden yukarıdaki durum oluştuğunda yine sorun çıkacaktır.
java -classpath naughty.jar:foobar.jar… Foo
Çünkü naughty.jar foobar.jar dan once classpathe de yer aldığından Foo sınıfı içerisinden Bar sınıfı kullanılmak istendiğinde naughty.jar daki Bar sınıfı kullanılacaktır.
Açıkça Bağımlılıkların Belirtilme Eksikliği (Lack of Explicit Dependencies)
Bazı JAR dosyaları diğer JAR dosyalarına bağımlı olmadan işlevlerini yerine getirirler. Bununla birlikte çoğu JAR dosyası başka JAR dosyalarına ihtiyaç duyar. Ancak diğer jar dosyaları da deploy edilmiş ise kullanılabilirler. Örneğin Apache Jakarta Commons HttpClient jar ı Commons Codec ve Commons Logging jar a ihtiyaç duymaktadır. Dolayısıyle classpath de commons-logging.jar and commons-codec.jar olmaz ise çalışamaz.
Fakat biz bu bağımlılığı nasıl biliyoruz? Çünkü Httpclient iyi dokumente edilmiş ordan biliyoruz. Projenin web sayfasında bağımlılıklar açık bir biçimde belirtilmiştir. Fakat birçok library bu şekilde güzel bir biçimde dokumente edilmemiştir. Dolayısıyle belli olmayan böyle bir Jar ı kullandığımızda ClassNotFoundException alma ihtimalimiz yüksektir.
HttpClient daki gibi iyi dokumente edilse bile aslında yeterli değildir. Bizim istediğimiz;
-
standart bir yöntemle bağımlılıkların tanımlanması
-
tercihen Jar file içinde bu tanımın yapılması
-
tool larla da bu bağımlılıkların analiz edilebilmesidir.
Versiyonlama Eksikliği (Lack of Version Information)
Kütüphaneler değişmekte, geliştirilmekte ve zamanla yeni versiyonları çıkmaktadır. Bundan dolayı bir kütüphanenin bağımlı JAR larının hangileri olduğunun yanında bu JAR ların hangi versiyonlarına bağımlı olduğu bilgisi de gereklidir.
Örneğin bir JAR ımız, log4j jar a bağımlılığı olsun. Fakat JAR ımızın çalışması için, log4j nin hangi versiyonuna ihtiyaç duymakdır? (En son logj nin web sayfasına baktığımda 25 farklı versiyon gördüm 4 Haziran 2009 da). En son yani latest versiyonu kullanalım demek de mantıklı değil, çünkü son versiyona göre JAR ımız test edilmemiş olabilir.
JAR ımızın içerisinde “bu jar log4j nin 1.2 versionu üzerindeki jarlarla çalışabilir” ifadesi dokumante edilmiş ise güzeldir ama genelde böyle bir bilgi nadiren vardır olsa da toollar tarafından kullanılabilecek formatta olmayabilir.
Sonuç olarak bizim açıkça JAR ımızın hangi versiyon aralığındaki jar lara bağımlı olduğumuzu bir şekilde belirtmemiz gerekmekte.
Versiyonlar aşağıdaki diğer bir probleme daha yol açmaktadır.
Uygulamamızın şekildeki gibi A ve B adlı iki kütüphaneye ihtiyaç duyduğunu düşünelim. Ayrıca A ve B kütüphanelerinin de üçüncü parti diğer bir kütüphane olan C kütüphanesine bağımlı olsun. Fakat A, C ‘nin 1.2 , B de C nin 1.1 versiyonuyla çalışabilsin.
Classpath=C1.2.jar:C1.1.jar
Global classpath sahip olduğumuzdan C nin iki versiyonunu da classpathe eklediğimizde, classpath e eklediğmiz ilk versiyonu hem A hem de B kullanmak zorunda kalacaktır.
Burada dikkat edilmesi gereken C nin büyük versiyonu, numarası yüksek olduğundan dolayı kullanılacaktır diye bir şey yok eğer classpath de büyük versiyon öndeyse kullanılacaktır. Eğer küçük olan versiyon classpath de önceyse o kullanılacaktır.
Bununla birlikte 1.1 deki bazı sınıflar da eğer isimleri 1.2 dekinden farklı ise yine kullanıma açık olacaktır. Ve eğer bu sınıflardan birisi kullanılırsa bu sınıfın da kullandığı başka bir sınıf varsa ve eğer 1.2 de bu kullandığı sınıfın yeni versiyonu varsa onu kullanmaya çalışacak ve LinkageError gibi bir hata alma ihtimali doğacaktır.(aşağıdaki naughty.jar da olan duruma benzer bi durum)
JAR lar arasındaki Bilgi Saklama Eksikliği (Lack of Information Hiding Across JARs)
Bütün OOP dilleri information hiding için sınıf ve modül bazında belirli yöntemler sunarlar.
Java; sınıftaki üyeler (alanlar ve methodlar) için 4 farklı erişim belirleyicisi sağlar.
-
public üyeler tüm sınıflar tarafından erişilebilir.
-
protected üyeler alt sınıflar ve aynı paketteki diğer sınıflar tarafından erişilebilir.
-
private üyeler sadece aynı sınıfın içindekiler tarafından erişilebilir.
-
Yukarıdakiler erişim belirleyicilerinden herhangi birisiyle tanımlanmamış üyeler ise varsayılan olarak “default”olarak adlandırılan erişim belirleyicisine sahiptir. Bu üyeler aynı paketteki diğer sınıflar tarafından erişilebilirler.
Sınıflar için ise sadece public ve default erişim belirleyicisini kullanabilmekteyiz. public sınıflar diğer tüm paketlerdeki sınıflar tarafından erişilebilmekte, default sınıflar ise sadece aynı paketteki diğer sınıflar tarafından erişilebilmektedir.
Burada bir eksiklik var dikkat ettiyseniz. Erişim belirleyicileri paket seviyesine göre belirlenmekte fakat Java da deployment birimi paket değil Jar dosyasıdır. Birçok Jar dosyası birden fazla paketten oluşmaktadır. Ve aynı jar içindeki farklı pakette bulunan sınıflar biribirlerine erişme ihtiyacı duymaktadırlar. Bunu sağlamak için de ne yazıkki bu sınıflar public yapılmaktadır.
Özet olarak public yapılan sınıflar Jar ın dışındaki sınıflar tarafından da erişilebilmektedir. Bundan dolayı biz istemesek de JAR ımız public hale gelmiş oluyor. Bu da Jar dosyaların diğer bir sorunu olarak karşımıza çımakta.
Sonuç olarak : JAR lar Modül değildir!!!
Şimdiye kadar Jar dosyalarındaki belirli sorunları gördük. Tabi ki bu gördüklerimiz Java da modüler sistem oluşturamayacağımız anlamına gelmemekte. Sadece şu anda Java modüler sistem amacına ulaşmak için bir yardım sunmamakta. Bundan dolayı modüler sistem kurmak bir süreci ve disiplini gerektirmekte.
Java EE Sınıf Yükleme Mekanizması
Java EE specification; dağıtık ve çok katmanlı bir platform tanımlamaktadır. Java EE mimarisinin nin merkezi özelliği bir çok uygulama bileşenini yöneten ve bu bileşenlere transaction,security gibi kurumsal servisler sunan “uygulama sunucusu” bölümüdür.
Uygulama sunucuları ayrıca; server ı restart etmeden ve diğer uygulamaları etkilemeden uygulamaları deploy etmek, un-deploy etmek için bir deployment-sistemine ihtiyaç duymaktadırlar.
Bu ihtiyacı karşılamak için Java EE uygulama sunucuları her bir deploy edilen uygulama için biraz daha karmaşık, aşağıdakine benzer bir class-loading yapısı kullanmaktadırlar.
-
Java EE uygulamaları EAR dosyaları halinde deploy edilirler. Bu EAR dosyaları aşağıdakileri (ilki ve diğerlerinden bir ya da birkaçını) içeren zip dosyalarıdır;
-
Metadata dosyası, application.xml;
-
Düz Java dosyalarını içeren Jar dosyası
-
EJB uygulamalarını içeren Jar dosyaları (EJB-JARs)
-
Web işlevlerini yerine getiren sınıfları, Servlet-JSP gibi, içeren Web Archive dosyası (WAR)
-
Düz Java dosyalarını içeren Jar dosyaları; EAR içindeki tüm EJB-JAR ları ve WARları tarafından erişilmesi düşünülen sınıfları içermektedirler. Bu nedenle EAR Class Loader tarafından bu sınıflar yüklenmektedir.
-
Önceden anlattığımız class loading mekanizmasını bir daha hatırlayacak olursak. Bir class loader sadece, kendisi ya da atası tarafından tanımlanmış sınıfları yükleyebilmektedir. Yani ek olarak kardeşlerini (siblinglerini) yükleyememektedir. Bu nedenle EJB ve WAR tarafından paylaşılan ya da kullanılan sınıflar EAR Class Loader a konulması gerekmektedir.
-
Ayrıca eğer deploy edilen birçok uygulama tarafından bir sınıf kullanılacaksa bu sınıfın system application loader seviyesine konulması gerekmektedir. Bu da application server ın restart edilmesine ve JAR dosyasının global classpath e eklenmesine neden olmaktadır.
-
system application loader seviyesine çıkarılan libraryler;
-
kullanabilmek için restart gerekli
-
Biz sadece deploy edilen bir kaç uygulamanın kullanmasını diğerlerinin kullanmamasını istediğimiz halde, deploy edilen tüm uygulamalar için kullanılabilir hale gelecektir.
-
Sınıf çakışmalarına (Class conflict) neden olacaktır.
-
Üst seviyede bunulan sınıflar daima alt tarafta bulunan aynı sınıflara göre öncelikliği olduğundan alttaki sınıflar ezilecektir.
-
Serverda bulunan tüm uygulamalar üstte bulunan kütüphanenin aynı versiyonunu kullanmak zorunda kalacaktır.
Bu nedenlerden dolayı Java EE geliştiricileri farklı uygulamalar tarafından kullanılan ortak sınıflar tanımlamaktan uzak durmaya çalışmaktadırlar. Birden fazla uygulama tarafından ortak kullanılacak bir kütüphane olduğunda da bu kütüphane herbir EAR içine ayrı ayrı konularak çoğaltılmaktadır. Sonuç olarak “boru” şeklinde düşey olarak etkileşim kurmakta yatayda etkileşim kuramamaktadırlar.
OSGi Teknolojisi -
OSGi, Java için bir modül sistemidir.
-
Gerçek anlamda bir modül oluşturmamızı ve bu modüllerin çalışma zamanında birbirleriyle etkileşimde bulunabilmesi için yöntem sunar.
-
Temel olarak felsefesi basittir:
-
Java da bir çok sorunun kaynağı global ve düz olan classpath ten dolayıdır. OSGi bu nedenle tamamen farklı bir yaklaşım sunmaktadır.
-
Her bir modül kendi classpath ine sahiptir. Bu birçok problemi ortadan kaldırmaktadır.
-
Peki paylaşılan sınıflar ne olacak?
-
OSGi; sınıfların modüller arasında nasıl paylaşılabileceğine dair açıkça belirtilen “import ve export” mekanizmasını kullanmaktadır.
-
Peki OSGi da modüle neye benziyor? OSGi da aslında modül ismi yerine başka bir kelime kullanılmakta: bundle . Bundle da aslında bir JAR dosyası. Yani yeni bir standart tanımlanmıyor. Sadece, JAR dosyasına metadata eklenerek bundle haline getirilmektedir.
-
Metadata şunlardan oluşmakta:
-
Bundle ın adı.
-
Bundle versiyonu.
-
import lar ve exportlar
-
İsteğe bağlı olarak, bu bundle ın minimum hangi java versiyonu ile çalışabileceği.
-
Ve bundle ile ilgili diğer bilgiler (üretici adı, adresi, telif hakkı v.b)
-
Bu bilgiler Jar dosyası içerisinde MANIFEST.MF adındaki dosyada bulunmaktadır.
Ağaçtan Çizgeye (Graph) Geçiş
-
Peki herbir bundle için ayrı classpath ne anlama geliyor?
-
Çok basit: herbir bundle için bir classLoader sağlanmaktadır. Bu ClassLoader JAR dosyası içerisindeki sınıfları ve kaynakları (image v.b) görebilmektedir.
-
Bununla birlikte bundle ların beraber çalışabilmesi için de bir bundle daki ClassLoader ın gelen istemi diğer bundle ın ClassLoader ına delege etmesi gerekmektedir.
-
Java ve Java EE de ClassLoader mekanizmasının hiyerarşik bir ağaç şeklinde olduğunu anlatmıştık. Ve burada ClassLoading istemleri hep üst sınıfa delege edilmekte idi. Ayrıca burada sınıflar yatay olarak paylaşılamamakta idi. Paylaşılmak için bir üst ClassLoader ın yükleyeceği yere sınıfları koyuyorduk fakat bu da herkesin bu sınıflara erişimine neden oluyordu.
-
Sonuç olarak ağaç yanlış bir yapı classloading için. Bizim ihtiyaç duyduğumuz şekil graph (çizge).
-
İki bundle arasındaki bağımlılık hiyerarşik değil. Baba, çocuk yapısı yok. Sadece sağlayıcılar ve kullanıcılardan oluşan bir ağ var.
-
ClassLoading istemleri; bir bundle dan diğer bundle a, bundle lar arasındaki bağlımlılık ilişkisine (depedency relationship) göre delege edilmektedir.
Yukarıdaki şekilde 5 adet birbiriyle bağımlılıkları da gösterilen bundle gözükmektedir. Bundle lar arasındaki bağlar import ve export edilen paketler üzerinde kurulmuştur.Yani java paketi javax.swing ya da org.apache.log4j gibi.
-
B bundle ının org.foo adlı pakete sahip olduğunu düşünelim. Eğer bu paketi dışarıya açmak istiyorsa MANIFEST.MF dosyasında export ile belirtilen satırda bu paketin adını belirtir.
-
A bundle ı da eğer B bundle ındaki bu org.foo bundle ını kullanmak istiyorsa MANIFEST.MF dosyasında import ile belirtilen satırda bu paketin adını ekler.
-
Bundan sonrası yani import takilerle export takilerin eşleşip eşleşmediğinin kontrolünü OSGi framework yapacaktır. Bu işleme “resolution process” denilmektedir ve çok karmaşık bir yapısı vardır. Bundle geliştiricisi olarak bizim yapacağımız sadece import ve exportları belirtmektir gerisini OSGi framework yapacaktır.
-
import daki bir değer export daki bir değer ile eşleşir eşleşmez bu ilişkideki bundle lar belirtilen paket ismi ile birbirlerine bağlanmış olurlar (wired). Bu ne anlama geliyor? Bundle A da, org.foo paketi içindeki bir bir sınıfın yüklenmesi istemi geldiği zaman bu istem hemen Bundle B deki Class Loader a delege edilir.
-
Aynı durum A nın ilişkide oluduğu C bundle ı için de düşünülebilir. Yani eğer C deki org.bar paketi kulanılmakta ve Bundle A da, org.bar paketi içindeki bir bir sınıfın yüklenmesi istemi geldiği zaman bu istem hemen Bundle C deki Class Loader a delege edilir.
-
Bu durum son derece etkindir. Klasik Java da ClassLoader çok uzun bir sınıf listesinde arama yaparken OSGi Class Loader ları; sınıfı nerede bulacağını genellikle hemen bilmektedir, çok az arama yaparak ya da hiç arama yapmadan.
-
Peki “resolution” süreci başarısız olursa ne olmaktadır? Örneğin uygulamamıza Bundle B yi yüklemeyi unutursak? org.foo paketini kullanan Bundle lar da ne olacaktır?
-
Bu durumda A bundle ı çözümlenemeyecektir ve kullanılamayacaktır.
-
Ayrıca gayet açıklayıcı bir biçimde A Bundle ının neden kullanılamayacağına dair bir hata mesajı alırız.
Versioning and Side-by-Side Versions
OSGi sadece paket ismine göre bağımlılıkları tanımlamamıza olanak sağlamamaktadır. Aynı zamanda paketin versiyonuna göre de bağımlılıklar tanımlanabilmektedir.
Export edilecek paket adları bir versiyon özelliği ile birlikte tanımlanmaktadır. Fakat import lar versiyon aralığı ile tanımlanmaktadır. Bu bize şunu sağlamaktadır: Örneğin bundle ımız x paketinin 1.2.0 ile 1.4.3 versiyonu arasındakilere bağımlıdır. Eğer paketin bulunduğu bundle da bu aralıkta bir export yok ise o zaman bizim bundle ımız çözümlenemez ve neden çözümlenemeyeceğine dair açıklayıcı bir hata mesajı alırız.
Dikkat ettiyseniz aynı kütüphanenin farklı versiyonları uygulamamızda bu sayede kullanabilmekteyiz.
Dinamik Modül
-
OSGi, sadece Java için bir modül sistemi değildir aynı zamanda dinamik bir modül sistemidir.
-
OSGi daki bundle lar; uygulamamızın tümünü etkilemeden install, update ya da uninstall edilebilmektedir.
-
Bu bize serverdaki uygulamımızın bir parçasını değiştirmeyi, bir bug ı çözerek ya da yeni bir özellik ekleyerek, diğer parçalar etkilenmeden yapabilme imkanı sağlamaktadır.
-
Ayrıca desktop uygulamalarımızda; server ı restart etmeden yeni versiyonları download edebilmemizi böylece kullanıcıyı kesintiye uğratmadan uygulamayı kullanabilmesini sağlamaktayız.
-
Dinamik modül özelliğini sağlamak için OSGi lifecycle-layer a sahiptir.
-
Birçok geliştirici OSGi ın dinamik modül özelliğine kuşkuyla bakmaktadırlar ve OSGi ın life-cycle layer ını önemsemeyerek sadece modülerlik kazancını önemsemektedirler. Tabi ki bu durum Java EE daki güvenilmeyen “hot deployment” özelliğinden dolayı mantıklı görülebilir. Fakat OSGi ın dinamik modül özelliği bir aldatmaca değildir. Gerçekten çalışmaktadır ve bazı denemeler de bunu doğrulamaktadır.
The OSGi Alliance and Standards -
OSGi standartları yaklaşık 40 şirketin yer aldığı bir organizasyon tarafından tanımlanmaktadır.
-
OSGi ile ilgili en çok sorulan iki soru?
-
OSGi açılılımı ne demektir?
-
Neden i harfi küçük?
Cevap olarak. OSGi açılım olarak bir anlamı yok. Önceden Open Services Gateway initiative olarak adlandırılmakta idi. Küçük i harfi de buradan gelmekte, uzun isimde i nin biçimsel olarak yeri yoktu. Fakat uzun isim sonradan kullanım dışı oldu.
-
OSGi “home gateway” yani evdeki tüm cihazları tek bir noktadan kontrol edebilme amacıyla başta geliştirilmiş olsa da artık genişleyerek çok daha geniş bir alanda kullanılmaktadır.
-
Sonuç olarak OSGi ismi tuhaf gelebilir. Fakat anlamlı bir kelime ya da bir kelimenin parçası olmadığından google da arama yapabilmek için güzel bir isimlendirme
-
Konuşurken de (“Oh Ess Gee Eye”) diye okunmaktadır. Ozjee diye okunmaz.
-
OSGi Alliance ın rolü;
-
Specificationları tanımlamak
-
Specification a göre geliştirilen gerçekleştirimleri certified etmek (onaylamak).
-
Teknik işler; birçok sayıdaki Expert Group (EG) tarafından yapılmaktadır.
-
Core Platform Expert Group (CPEG),
-
Mobile (MEG),
-
Vehicle(VEG)
-
Enterprise (EEG) Expert Groups.
Bu dokumanda , biz daha çok CPEG in yaptığı işlere değindik.
OSGi Implementations
Günümüzde birbirinden bağımsız olarak geliştirilen birkaç tane OSGi frameworkü bulunmaktadır. Bunlardan open-source olarak geliştirilen 4 tanesi:
-
Equinox
-
OSGi Specification ın 4.1 relase ini implement etmiştir.
-
IBM Webshere Application Server, Lotus Notes gibi uygulamalar kullanmıştır.
-
EPL (Eclipse Public Licence)
-
Knopflerfish
-
OSGi Release 3 ve Relase 4.1 specification ın gerçekleştirimidir.
-
BSD Lisans.
-
Felix : Apache
-
OSGi Release 4.x specificationların gerçekleştirimidir
-
Apache Lisans
-
Concierge
-
OSGi Relase 3 ün optimize edilmiş implementasyonudur.
-
Optimize edildiği için kaynak kısıtlı örneğin mobil uygulamalar için uygundur.
OSGi Alternatifleri
OSGi, getirdiği güzel yaklaşımlara göre karmaşıklığı basittir. Diğer bazı gerçekleştirimler OSGi kadar olgunlaşmamış ya da yaygın kullanılmamıştır. Fakat bazı alternatiflere bakalım ki en azından OSGi ın geliştirilirken etkilendiği güzel fikirleri görmüş oluruz.
Build Tools: Maven and Ivy
-
Maven ve Ivy modüler sistemin bazı özelliklerine sahiptir.
-
Fakat bu araçlar build-time araçlarıdır run-time değillerdir.
-
Run-time aracı olmadıklarından JAR daki Global classpath, information hiding gibi sorunlara çözüm getirememektedirler.
-
Doğrudan OSGi ile karşılaştırılamaz, aslında OSGi temelli uygulama geliştirmek için kullanılan yardımcı araçlardır.
Eclipse Plug-in System
-
Şu anda Eclipse bir OSGi implementation ı olan Equinox tabanlıdır.
-
Fakat Eclipse 3.0 dan önce kendi modül sistemine sahipti. Eclipse de modül e plug-in denilmekte idi. Plug-in ler de plugin.xml dosyasını içermekteydi. Bu dosya OSGi daki MANIFEST.MF e çok benzer bir yapıdaydı.
-
OSGi bundle ile olan fark şurda : OSGi da bağımlılıklar paket seviyesinde tanımlanırken, Eclipse plug-in de bağımlılıklar tüm plug-in bazında tanımlanmaktadır. Böylece plug-in deki tüm paketlere erişebilmektedir. OSGi da tüm bundle a bağımlılığı sağlamakla birlikte , bu özelliği kullanmanın bazı sakıncaları vardır.
-
Eclipse plug-in sistemindeki en büyük eksiklik plug-in lerin dinamik bir biçimde install ve uninstall edilememesidir. Plug-in graph i değişir değişmez full restart gerekmektedir. Equinox projesiyle eclipse de dinamik olarak plug-in sistemini yapmak için uğraşmış ve sonunda bu sistem için OSGi seçilmiştir.
-
JSR 277
-
Java Specification Request 277 diğer bir adıyla “Java Modüle System” dir.
-
OSGi ın uğraştığı problemlerin aynısına JSR da çözüm getirmeye çalışmaktadır.
-
Fakat JSR bitirilmemiş bir specification ve implementation I vardır.
-
Java 7 de bitirilmesi düşünülüyordu fakat henüz belli değil.
-
Eclipse plug-in sistemi gibi paket seviyesinde bağımlılığı desteklememektedir.
-
Dinamik değildir, JVM nin yeniden restart edilmesi gerekmektedir.
Sonuç ve Değerlendirme
Java şu anki yapısıyla dinamik modül sistemini sağlamamaktadır. OSGi teknolojisi ile dinamik bir modül sistemi oluşturulmakta ayrıca Java daki classpath, JAR problemlerine çözüm getirilmektedir.
Kaynaklar -
OSGi In Practice - Neil Bartlett
-
http://www.osgi.org
Dostları ilə paylaş: |