Od czasu do czasu zdarza się “stoper”, który potrafi zmarnować pół dnia. Taki ostatnio mi się przydarzył podczas deploy’u aplikacji, ale po kolei:
Mamy ear zawierający dwa ejb-jar’y. Zarządzanie zależnościami za pomocą maven’a. Obydwa archiwa ejb-jar korzystają do budowy z pluginów maven’a: maven-compiler-plugin oraz maven-ejb-plugin.
Pierwszy ejb-jar udostępnia pewnego bean’a z lokalnym interfejsem biznesowym – po prostu standard.
@Stateless @Local(Service.class) public class ServiceBean implements Service { public void test(String param) { //do sth. with param } (...)
Drugi ejb-jar chce skorzystać z usług ServiceBean. Do wstrzyknięcia usługi korzystamy więc z pomocy adnotacji @EJB. Ejb-jar jako zależność podane ma archiwum z API.
@Stateless public class ClientBean implements ClientInterface { @EJB private Service service; public void clientMethod(String param) { service.test(param); } (...)
Interfejs Service wyciągnięty został do jar’a z API. Obydwa ejb-jar’y mają podany API jako zależność w pliku pom.xml
<dependency> <groupId>eu.krzych.test</groupId> <artifactId>service-core-API</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
Podczas deploy’u aplikacji serwer łaskawie zezwala na użycie ServiceBean’a w pierwszym ejb-jar. Kiedy jednak dochodzi do uruchomienia drugiego ejb otrzymujemy taki mniej więcej wyjątek:
ERROR (...) JBAS014134: EJB Invocation failed on component ClientBean for method public abstract void eu.krzych.ClientBean.clientMethod(java.lang.String): javax.ejb.EJBException: java.lang.IllegalStateException: JBAS011048: Failed to construct component instance
Ogólnie narzekanie, że nie można utworzyć instancji ServiceBean’a w drugim archiwum ejb-jar. Dziwne, przecież wszystko zgodnie z naukami :/
Tutaj kłania się maven z pluginem maven-ejb-plugin oraz jego zdolność do konstruowania plików MANIFEST.MF. Okazało się, że plugin dodał jako zależność OSGi plik z moim API serwisu.
Pierwsza aplikacja załadowała klasy API, utworzyła beana i udostępniła go za pomocą interfejsu lokalnego – wszystko ok.
Następnie druga aplikacja ponownie załadowała klasy API i spróbowała wykorzystać udostępnionego beana, co zakończyło się wyjątkiem. Classloader’y odrobinkę poszalały.
Rozwiązanie? Banalne – wystarczy podać w drugiej aplikacji zależność do API w kontekście już dostarczonej biblioteki (ang.<scope>provided</scope>). Powstrzyma to maven’a, przed dodaniem zależności do pliku MANIFEST.MF. Czyli po zmianie wpis zależności wygląda tak:
<dependency> <groupId>eu.krzych.test</groupId> <artifactId>service-core-API</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency>