2015-05-15 5 views
12

Ich bin eine Gradle-basierte Java SE-Anwendung auf Hibernate als mein ORM der Wahl gebaut. Mein Plan ist, weld-se zu verwenden, CDI-Anmerkungen für Einspritzungen von EntityManagers während der Anwendung zu verwenden.Bean Discovery Probleme bei der Verwendung von weld-se mit Gradle Anwendung Plugin

Auf der Grundlage der gemeinsamen HibernateUtil Hilfsklasse in der Dokumentation Hibernate gefunden, zog ich in Richtung JPA-Schnittstellen und hinzugefügt @Produces Anmerkungen Produzenten Methoden zur Verfügung zu stellen (ich habe eine leeres META-INF/beans.xml ebenfalls berücksichtigt):

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(); 
     } 
    } 
} 

Als ich versuchen, die @Inject Anmerkung auf einem Feld zu verwenden, jedoch nicht Weld die richtige Producer-Methode und erzeugt eine Ausnahme statt zu beheben:

Exception in thread „main“ org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001308: Kann keine Beans für Type: class app.DemoApplication auflösen; Qualifier: [@ javax.enterprise.inject.Any()] bei org.jboss.weld.bean.builtin.InstanceImpl.get (InstanceImpl.java:101) bei app.Main.main (Main.java:14

)

der säumige Code wird für CDI Unterstützung durch den Weld Behälter instanziiert und ist unglaublich basic:

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(); 
     } 
    } 
} 

Bin ich hier einen offensichtlichen Punkt fehle? Wie kann ich Weld dazu bringen, die Producer-Methode zum Injizieren meiner Abhängigkeiten zu finden?

Ich habe ein minimales Projekt zusammengestellt, das mein Problem auf Github reproduziert. Danke für hilfreiche Vorschläge! :)

-Update 2015.05.18:

Scheint, wie ich die Fehlermeldung falsch verstanden. Tatsächlich löst Weld nicht einmal die Bean DemoApplication, was zu der Annahme führt, dass mit dem Bean-Discovery-Prozess etwas nicht stimmt. meine Schweiß-se Abhängigkeit auf die frisch frei 3.0.0.Alpha8 Version (siehe die verknüpfte Github Repo) Nach der Aktualisierung konnte ich die Anwendung erhalten, indem manuell zu sagen Weld über meine Bohnen in Main.java zu arbeiten:

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

Dennoch sind alle Vorschläge, warum die Bohnen nicht automatisch entdeckt werden, obwohl sie leer sind META-INF/beans.xml, sehr geschätzt!

-Update 2015.05.19:

Das Geheimnis enträtselt ist, meine eigene Antwort siehe unten. Ich habe den Titel der Frage geändert, um die tatsächliche Art des Problems wiederzugeben.

+0

Was ist der "Bean-Discovery-Modus" in Ihrer 'beans.xml'? –

+0

@MilanBaran Ich habe versucht, eine leere beans.xml, sowie eine mit 'bean-discovery-mode = ALL', beide ohne sichtbaren Effekt. – AdrianoKF

Antwort

14

Nachdem ich mehr Zeit verbracht habe, als bei einem solchen Problem vernünftig zu sein scheint, war ich endlich in der Lage, den Ursprung meines Problems zu finden. Es hat weder mit Weld noch jandex, sondern die Art und Weise Gradle Strukturen seiner Ausgabeverzeichnisse zu tun:

Das :build Task erstellt zwei getrennte Ausgabeordner für die tatsächlichen Übersetzungsergebnisse und und für zusätzliche Ressourcen (build/classes und build/resources). Nur wenn Sie ein JAR-Archiv erstellen, werden diese beiden Ordner zusammengeführt. Der Task :run startet die Anwendung jedoch direkt aus den Kompilierungsausgabedateien mit zwei separaten Klassenpfadeinträgen für Klassen und Ressourcen.

Der Bean-Erkennungsmechanismus von Weld versucht offenbar nur, Beans für den gleichen Klassenpfadeintrag wie die META-INF/beans.xml-Datei zu finden, die in diesem Fall der Ordner build/resources/main ist. Im Gegenzug werden keine Bohnen jemals entdeckt und sind nirgendwo injizierbar.

für jetzt meine Abhilfe (siehe Git-Repository) ist eine zusätzliche Gradle Aufgabe zu erstellen, die Ressourcen in die entsprechenden Ordner zu kopieren, so dass Bohne Entdeckung auf dem richtigen Classpath Eintrag passiert:

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

processResources.dependsOn copyResources 

die gleiche Problem mit einem ähnlichen wurde in der Gradle Foren beschrieben: 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

Vielen Dank an alle für Ihre Hinweise!

+0

Danke Mann ... Sie sind ein Lebensretter – magicroomy

+0

Haben Sie ein [WELD] (https://issues.jboss.org/projects/WELD) Problem über dieses seltsame Verhalten eingereicht? – lucasvc

+0

Anstatt zu kopieren, könnten Sie den Ressourcenordner hinzufügen, um den Klassenpfad der Aufgabe auszuführen. 'run { Klassenpfad + = Dateien (" $ {BuildDir}/Ressourcen ") } ' – serdroid

2

Es gibt ein Problem mit static Deklaration. Könnten Sie das auf nicht statische Weise tun?

In Bezug auf https://issues.jboss.org/browse/WELD-1505 sollte in 2.2.0 behoben werden.Beta2

+0

Ich habe versucht, die statischen Producer-Methoden in HibernateUtil in Instanzmethoden zu konvertieren, allerdings ohne Erfolg. Ich habe sowohl Weld 2.2.9.Final als auch 3.0.0.Alpha8 ausprobiert, daher sollte das von Ihnen erwähnte Problem in diesen Versionen behoben sein. Danke für den Vorschlag noch! – AdrianoKF

1

Soweit ich von Ihrem GIT Beispiel sehen konnte, haben Sie so etwas wie dies in Bezug auf die Hierarchie der Objekte:

Main -> DemoApplication -> EntityManager 

Wenn Sie eine EntityManager auf Ihre DemoApplication sind Injektion, Sie haben um die DemoApplication auf Ihre Main.java zu injizieren, sonst brechen Sie die Injektionskette.

Versuchen Sie, Ihre DemoApplication und HibernateUtil Klasse mit @Singleton und dann spritzen Sie es auf Ihren Main.java Klasse zu annotieren:

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

Das manuelle WELD Bohne Erkennung überspringen helfen sollte.

Wenn Sie vorhaben, ein JAR zu erstellen, sollte Ihre beans.xml in META-INF sein. Wenn Sie jedoch ein WAR aus Ihrem Projekt herausnehmen, muss sich die beans.xml in WEB-INF befinden .

1

Die Lösung könnte auch gefunden werden, indem man SourceSets in Gradle definiert.

So:

// 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' 
    } 
} 

Sieht cleaner meiner Meinung nach. Und sicherlich besser, als jede Klasse als Paket zu deklarieren.

0

Als eine Verbesserung: Gradle 4 verwendet ein anderes Ausgabeverzeichnis für die Klassen.

Die Taskdefinition in other answer ist etwas falsch. Mit Blick auf gradle Protokoll für Lauf Aufgabe, hier ist der Classpath verwendet:

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....... 

So nutzt es /build/classes/java/main und dann /build/resources/main.Hier ist meine Aufgabe:

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

processResources.dependsOn copyResources 
+0

Verbesserung. Gradle 4 verwendet ein anderes Ausgabeverzeichnis für die Klassen –

+0

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

Verwandte Themen