2015-05-15 12 views
12

Buduję aplikację Javy opartą na Gradle opartą na gradacji Hibernate jako ORM z wyboru. Moim planem jest użycie weld-se, aby móc korzystać z adnotacji CDI do iniekcji EntityManagers w całej aplikacji.Problemy z odkryciem fasoli podczas korzystania z funkcji spawania z wtyczką aplikacji Gradle

podstawie wspólnej klasy HibernateUtil pomocnika znaleźć w dokumentacji hibernacji, przeniosłem się do interfejsów WZP i dodał @Produces adnotacje dostarczenie sposobów wytwórca (Dodałem pusty META-INF/beans.xml również):

package dao; 

import javax.enterprise.inject.Disposes; 
import javax.enterprise.inject.Produces; 
import javax.persistence.EntityManager; 
import javax.persistence.EntityManagerFactory; 
import javax.persistence.Persistence; 

public class HibernateUtil { 
    private static final EntityManagerFactory emf = buildEntityManagerFactory(); 

    private static EntityManagerFactory buildEntityManagerFactory() { 
     try { 
      return Persistence.createEntityManagerFactory("persistenceUnit"); 
     } catch (Throwable ex) { 
      System.err.println("Initial EntityManagerFactory creation failed." + ex); 
      throw new ExceptionInInitializerError(ex); 
     } 
    } 

    @Produces 
    public static EntityManager createEntityManager() { 
     return emf.createEntityManager(); 
    } 

    public static void closeEntityManager(@Disposes EntityManager em) { 
     System.out.println("Closing EM"); 
     try { 
      em.close(); 
     } catch (Throwable t) { 
      t.printStackTrace(); 
     } 
    } 
} 

Kiedy spróbuj użyć @Inject adnotacji na polu, jednak nie rozwiązuje Weld właściwą metodę producent i wywołuje wyjątek zamiast:

wyjątek w wątku „main” org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001308: Nie można rozpoznać żadnych komponentów bean dla Type: class app.DemoApplication; Kwalifikatory: [@ javax.enterprise.inject.Any()] at org.jboss.weld.bean.builtin.InstanceImpl.get (InstanceImpl.java:101) na app.Main.main (Main.java:14)

Kod wykraczająca jest tworzony przez pojemnik Weld wsparcia CDI i jest bardzo podstawowe:

package app; 

import javax.inject.Inject; 
import javax.persistence.EntityManager; 

public class DemoApplication { 
    @Inject private EntityManager em; 

    public void run() { 
     try { 
      em.getTransaction().begin(); 
      System.out.println("Inside transaction"); 
     } catch (Throwable t) { 
      t.printStackTrace(); 
     } finally { 
      em.getTransaction().rollback(); 
      em.close(); 
     } 
    } 
} 

jestem brakuje oczywisty punkt tutaj? Jak mogę sprawić, aby Weld odkrył metodę producenta do wstrzykiwania moich zależności?

Połączyłem minimalny projekt odtwarzający mój problem na Github. Dzięki za wszelkie pomocne sugestie! :)

Aktualizacja 18.05.2015:

Wydaje się, że źle się komunikat o błędzie. W rzeczywistości Weld nawet nie rozwiązuje fasoli DemoApplication, co prowadzi mnie do przekonania, że ​​coś jest nie tak z procesem odkrywania fasoli. Po zaktualizowaniu mojego uzależnienia spoiny-se świeżo wydany 3.0.0.Alpha8 wersji (patrz powiązany GitHub repo), udało mi się dostać do pracy aplikację ręcznie mówi Weld o moich fasoli w Main.java:

final Weld weld = new Weld() 
     .enableDiscovery() 
     .addPackage(false, HibernateUtil.class) 
     .addPackage(false, DemoApplication.class); 

Nadal bardzo cenne są wszelkie sugestie, dlaczego ziarna nie są automatycznie wykrywane, mimo że puste META-INF/beans.xml są na miejscu!

Aktualizacja 19.05.2015:

Tajemnica jest niewyjaśniona, zobacz moje własne odpowiedzi poniżej. Zmieniłem tytuł pytania, aby odzwierciedlić rzeczywisty charakter problemu.

+0

Co to jest 'bean-discovery-mode' w twoim' beans.xml'? –

+0

@MilanBaran Próbowałem użyć pustego pliku beans.xml, a także jednego z 'bean-discovery-mode = ALL', bez żadnego widocznego efektu. – AdrianoKF

Odpowiedz

14

Spędziwszy więcej czasu niż wydaje się to normalne w przypadku takiego problemu, w końcu udało mi się znaleźć przyczynę mojego problemu. Ma do czynienia ani z Weld ani Jandex, lecz sposób, w jaki Gradle struktury podkatalogów wyjściowe:

Zadaniem :build tworzy dwa oddzielne foldery wyjściowych dla rzeczywistych wyników kompilacji i i dodatkowych zasobów (build/classes i build/resources). Tylko wtedy, gdy utworzysz archiwum JAR, te dwa foldery zostaną scalone. Zadanie :run uruchamia aplikację bezpośrednio z folderów wyjściowych kompilacji z dwoma osobnymi wpisami klasy classpath dla klas i zasobów.

Mechanizm wykrywania fasoli spawacza najwyraźniej próbuje odkryć komponenty bean dla tego samego wpisu ścieżki klasy, co plik META-INF/beans.xml, który w tym przypadku jest folderem build/resources/main. Z kolei żadna fasola nie jest nigdy odkryta i nigdy nie kwalifikują się do iniekcji w dowolnym miejscu.

Moja obejście teraz (patrz repozytorium GIT) jest stworzenie dodatkowego zadania Gradle do kopiowania zasobów do odpowiedniego folderu, tak że fasola odkrycie dzieje się na odpowiednim wejściu ścieżce klas:

task copyResources(type: Copy) { 
    from "${projectDir}/src/main/resources" 
    into "${buildDir}/classes/main" 
} 

processResources.dependsOn copyResources 

To samo Problem z podobnym został opisany na forach Gradle: https://discuss.gradle.org/t/application-plugin-run-task-should-first-consolidate-classes-and-resources-folder-or-depend-on-installapp-or-stuff-like-weld-se-wont-work/1248

Dziękuję wszystkim za wskazówki!

+0

Dzięki człowieku ... jesteś oszczędzającym na życie – magicroomy

+0

Czy zgłosiłeś/aś jakieś [WELD] (https://issues.jboss.org/projects/WELD) zagadnienie dotyczące tego dziwnego zachowania? – lucasvc

+0

Zamiast kopiować można dodać folder zasobów, aby uruchomić ścieżkę klasy zadania. 'run { classpath + = pliki (" $ {buildDir}/resources ") } ' – serdroid

2

Wystąpił problem z deklaracją static. Czy możesz to zrobić w sposób nie statyczny?

Odnośnie do https://issues.jboss.org/browse/WELD-1505 należy ustalić w 2.2.0.Beta2

+0

Próbowałem już konwersji metod statycznego producenta w HibernateUtil na metody instancji, jednak bezskutecznie. Próbowałem zarówno Weld 2.2.9.Final i 3.0.0.Aphpha8, więc problem, o którym wspomniałeś, powinien zostać naprawiony w tych wydaniach. Jeszcze dziękuję za sugestię! – AdrianoKF

1

ile mogłem zobaczyć z przykładu GIT, masz coś takiego w kategoriach hierarchii obiektów:

Main -> DemoApplication -> EntityManager 

Jeśli wstrzykiwanie EntityManager na swojej DemoApplication, musisz wstrzyknąć aplikację DemoApplication na Main.java, w przeciwnym razie przerwiesz łańcuch wstrzykiwania.

Spróbuj opisywania DemoApplication i klasę HibernateUtil z @Singleton a następnie wstrzyknąć go na swojej klasie Main.java:

@Inject private EntityManager entity; 
@Inject private DemoApplication demo; 

To powinno pomóc pominąć ręczne wykrywanie WELD Bean.

Jeśli masz zamiar wyprodukować plik JAR, plik bean.xml powinien znajdować się w formacie META-INF, ale jeśli usuwasz WAR z projektu, plik bean.xml musi znajdować się w WEB-INF .

1

Rozwiązanie można również znaleźć, definiując sourceSets w gradle.

Jak to:

// Override Gradle defaults - a force an exploded JAR view 
sourceSets { 
    main { 
     output.resourcesDir = 'build/classes/main' 
     output.classesDir = 'build/classes/main' 
    } 
    test { 
     output.resourcesDir = 'build/classes/test' 
     output.classesDir = 'build/classes/test' 
    } 
} 

Wygląda czystsze moim zdaniem. I na pewno lepiej niż deklarowanie każdej klasy jako paczki.

0

Jako ulepszenie: Gradle 4 używa innego katalogu wyjściowego dla klas.

Definicja zadania w other answer jest nieco błędna. Patrząc na Gradle dziennika dla zadania run, oto ścieżka klasy używane:

Command: /home/rizalp/package/jdk1.8.0_141/bin/java -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -cp /home/rizalp/code/helloworld-cdi2-se/build/classes/java/main:/home/rizalp/code/helloworld-cdi2-se/build/resources/main:/home/rizalp/.gradle/caches/modules-2/files-2.1/org.glassfish.jersey.inject/jersey-cdi2-se/2.26-b09/c540e230762ab07ebb3d372ee13446419d70dd28/jersey-cdi2-se-2.26-b09.jar....... 

więc używa /build/classes/java/main a następnie /build/resources/main.Oto moje zadanie:

task copyResources(type: Copy) { 
    from "${projectDir}/src/main/resources/META-INF" 
    into "${buildDir}/classes/java/main/META-INF" 
} 

processResources.dependsOn copyResources 
+0

Ulepszenie. Gradle 4 używa innego katalogu wyjściowego dla klas –

+0

https://stackoverflow.com/a/30325614/1448852 –

Powiązane problemy