Eclipse / OSGI, Java 11, JAXB ve Classloader
İki Java modülüm var, A ve B. A, JAXB işleri yapmak için JAXB ek açıklamaları ve yardımcı sınıfları içeren bir çekirdek model (bağlam oluşturma, sıralama, unmarshalling, vb.) Sağlar B, modele @ aracılığıyla dahil edilen ek sınıfları sağlar XmlAnyElement (lax = true) ve bu nedenle JAXB bağlamına eklenmelidir.
Bu, düz Java'da sorunsuz çalışır - B'nin sınıf yükleyicisi, ilgili tüm sınıfları görür ve JAXB bağlamını şu şekilde başlatabilir:
JAXBContext.newInstance(RootFromA.class, RootFromB.class)
Şimdi OSGI ile aynı şeyi deniyorum (B bir Eclipse eklentisidir ve A, düz bir Java komut satırı modülü C tarafından da kullanılacak olan çekirdek kitaplıktır). Çok fazla deneme yanılmadan sonra, hem JAXB API'sini hem de OSGI paket ithalatı yoluyla uygulamayı görmek için A ve B'yi almayı başardım. Sorun şu ki, newInstance'ı yukarıdaki gibi çağırmak JAXB API'sinin sınıf yükleyicisini kullanıyor gibi görünüyor, RootFromA'yı değil ve kesinlikle RootFromB'yi değil. Bu nedenle, JAXB uygulamasını bile göremez ve ContextFactory sınıfını bulamadığından şikayet eder.
Bunu newInstance'ın farklı bir sürümünü arayarak çözmeyi başardım:
JAXBContext.newInstance(
RootFromA.class.getPackageName()
+ ":" + RootFromB.class.getPackageName(),
RootFromB.class.getClassLoader())
Bunu sevmiyorum, iki nedenden dolayı:
- "İstemci" kodum (B, A'daki JAXB yardımcı öğesinin istemcisidir) manuel olarak uygun bir sınıf yükleyici sağlamalıdır.
- Kodum bunların tamamen farkında olmasına ve aslında paket adını sınıflardan almasına rağmen, bağlam sınıflarını listeleyen tüm başvurulan paketlerde jaxb.index dosyalarını sağlamalıyım.
Muhtemelen (1) 'in etrafında bir yol yoktur, çünkü yalnızca B sınıfların tamamını bilir ve kimin sınıf yükleyicisinin hepsini görebileceğine karar verebilir. Eclipse uzantı noktaları aracılığıyla B'ye bağlanan ve JAXB bağlamı için ek sınıflar sağlayan C ve D uzantı modüllerini ekledikten sonra daha fazla sorunla karşılaşabileceğim için endişeleniyorum - B'nin sınıf yükleyicisi bunları görebilecek mi?
Ama (2) için gerekli olan statik indeks dosyalarından kurtulmanın bir yolunu bulabilirim. Tam bağlam sınıfları seti dinamiktir ve düz Java'da bir ServiceLoader tarafından ve Eclipse'de bir uzantı noktası tarafından kararlaştırılır. Her iki durumda da, bağlama ait olması gereken tüm sınıflara doğrudan erişime sahibim ve bu nedenle, her bir pakete fazladan jaxb.index dosyalarını manuel olarak eklemeyi ve dolayısıyla potansiyel ve gereksiz bir hata kaynağı eklemeyi düşünüyorum.
Bir şey mi kaçırıyorum? Bunu yapmanın "daha güzel" bir yolu var mı? Ve neden bir dizi bağlam sınıfını ve bir sınıf yükleyiciyi alan yeni birInstance yöntemi yok?
Yanıtlar
Görünüşe göre bir çözüm buldum. Benim sorunum, iki farklı amaç için sınıf yükleyicilerle uğraşmak zorunda kalmaktı:
- JAXB tarafından bağlamı somutlaştırmak için kullanılan sınıf yükleyici
- Çeşitli model sınıflarımın sınıf yükleyicileri
Yukarıda açıklandığı gibi, (1) JAXBContext.newInstance () içinde bir parametre olarak belirtilebilir, ancak yalnızca modeli tek tek model sınıfları yerine paket adları olarak belirtirken belirtilebilir. Bu, JAXB'nin sınıfları kendi başına araması gerektiği anlamına gelir ve bunu yapmak için kullanabileceği tek sınıf yükleyici (1) - birden fazla pakete yayılmışlarsa tüm model sınıflarını göremez.
Başka bir iş parçacığı ( JAXB, Apache Felix'in içinde çalışırken neden jaxb.index'imi bulamıyor? ) Bana, JAXB'nin bağlam gerçeklemesini bulmak için iş parçacığı bağlam sınıf yükleyicisini kullanacağını öğretti. Bunu, uygulamayı gören bir sınıf yükleyiciye (örneğin, kendi paketiminkini) ayarlamak, JAXB'nin kendi uygulamasını bulmasını sağlamak için yeterlidir, bu da benim tercih ettiğim newInstance () sürümünü bir dizi sınıfla çağırmak için özgür bırakır. Ve bu sınıflar zaten yüklendiğinden, JAXB bunları olduğu gibi kullanabilir ve farklı sınıf yükleyicileri umursamak zorunda değildir.
Kısacası:
Class[] myClasses = getModelClasses(); // gather all model classes, which may have different classloaders
Thread thread = Thread.currentThread();
ClassLoader originalClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader(getClass().getClassLoader()); // my own bundle's classloader
JAXBContext context = JAXBContext.newInstance(myClasses);
thread.setContextClassLoader(originalClassLoader); // reset context classloader
Bağlam sınıf yükleyici manipülasyon malzemesi bir AutoCloseable içine sarılabilir ve bu da bana JAXBContext somutlaştırmasını bir kaynakla deneme bloğunda kolayca sarmama izin verir.