2013-05-05 19 views
8

ho un filtro in cui sono dinamicamente classi delle servlet mappatura:Iniettare un EJB in una dinamica mappata servlet

@Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
     servletContext = filterConfig.getServletContext(); 

     File directory = getConventionDirectory(); 
     FileSystemInspector fileInspector = new FileSystemInspector(); 
     Set<ActionInfoData> actions = fileInspector.getActions(directory); 

     for (ActionInfoData action : actions) { 
      servletContext 
       .addServlet(action.getServletName(), action.getClassName()) 
       .addMapping(action.getServletMapping()); 
     } 

    } 

Poi quando accede un dato mappare il bean non viene iniettato.

@EJB 
    private I18nManager i18nManager; 

    @Override 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException { 
     I18nManager i18n = i18nManager; //null 
    } 

Se genero manualmente una mappatura nel web.xml dato EJB sta lavorando in quella servlet. Mi chiedo se il fatto che sto registrando i servlet in fase di esecuzione il contenitore non considera questi servlet come gestiti.

In questo caso, qual è il modo corretto di iniettare gli EJB nei miei servlet senza modificare il modo in cui vengono registrati dinamicamente tramite filtro?

È tramite JNDI l'unico modo per iniettare i miei EJB?

EDIT 1: ho cercato di implementare una classe ServletContextListener come suggerito da "Will" utilizzando il seguente codice nel web.xml:

<listener> 
     <listener-class>com.megafone.web.filter.convention.InitServlet</listener-class> 
    </listener> 

E la parte relativa alla realizzazione:

... 

@Override 
    public void contextInitialized(ServletContextEvent sce) { 
     ServletContext servletContext = sce.getServletContext(); 

     FileSystemInspector fileInspector = new FileSystemInspector(); 
     Set<ActionInfoData> actions = fileInspector.getActions(getConventionDirectory()); 

     for (ActionInfoData action : actions) { 
      servletContext 
       .addServlet(action.getServletName(), action.getClassName()) 
       .addMapping(action.getServletMapping()); 
     } 
    } 

... 

Sfortunatamente, il contenitore non si inietta gli EJB e rimane il puntatore nullo. Attualmente sto effettuando una ricerca JNDI sicura del tipo personalizzato per il servizio. Ovviamente questo è molto più costoso che usare l'iniezione corretta (correggimi se ho torto, non ho ancora fatto esperimenti sulle prestazioni).

Usando:
Java EE 6
JBoss AS 7.1

+0

Ottima domanda! – acdcjunior

risposta

3

Il problema sembra legato alla this reported bug che è ancora irrisolto. La risoluzione delle risorse funziona correttamente per Managed Beans come definito dalla specifica JSF, ma non per i bean gestiti CDI. Semplicemente annotare le vostre classi servlet dinamici con @javax.faces.bean.ManagedBean dovrebbe risolvere il problema (sì, proprio una soluzione abbastanza brutto):

@ManagedBean 
public class DynServlet extends HttpServlet { 
    private static final long serialVersionUID = 1L; 

    @EJB 
    private LoginService loginService; 

    public DynServlet() { 
     super(); 
    } 

    @Override 
    protected void doGet(HttpServletRequest request, 
      HttpServletResponse response) throws ServletException, IOException { 
     response.getOutputStream().println(
       "Request made to: " + getClass().getSimpleName()); 
     response.getOutputStream().println("Login Service: " + loginService); 

     return; 
    } 
} 

@WebListener 
public class DynamicServletLoadListener implements ServletContextListener { 

    public DynamicServletLoadListener() { 
     super(); 
    } 

    @Override 
    public void contextDestroyed(final ServletContextEvent contextEvent) { 
     return; 
    } 

    @Override 
    public void contextInitialized(final ServletContextEvent contextEvent) { 
     contextEvent.getServletContext().addServlet("dynservlet", DynServlet.class) 
       .addMapping("/services/dynservlet"); 
    } 
} 

Testato con JEE6 (OFC) e sia JBoss 7.1.1 e 7.2.0 (EAP 6.1. 0 alfa).

Edit:

Il problema con servlet mappati dinamiche è in realtà piuttosto profondo nell'architettura di base JBoss. Usano JBossWeb (una versione biforcuta di Tomcat) come implementazione del servlet e, nelle viscere del suo codice di gestione del contesto, stabilisce se instradare nuovi componenti tramite iniezione o regolare nuovo. A partire dalla data, i tuoi servlet dovrebbero essere annotati in qualche modo in modo che possano essere elaborati tramite l'iniezione: avevo menzionato @ManagedBean nella mia risposta originale ma sembra che anche l'annotazione con @WebServlet funzioni.

+0

Qualsiasi soluzione senza annotare ogni servlet? = ( –

+0

@FagnerBrack - non che ho trovato finora. Il problema è che non si ha accesso alla sorgente di servlet? O che semplicemente ce ne sono troppi? – Perception

+0

Attualmente sto creando una convenzione di mappatura per dinamicamente creare percorsi URI Http secondo un determinato jsp e posizione servlet (file system) .L'uso principale di Java EE è per la gestione back-end.In questo progetto non sto usando JSF a causa della natura restrittiva di esso e del leveraging HTML5 Boilerplate, jQuery, requirejs, bootstrap (personalizzato) e tutto il coolness front-end JSF non ha build in. Il problema principale nell'utilizzo dell'annotazione è che dovrei applicarlo su ogni classe in futuro invece di delegare tale attività per un processo automatico, solo comodità. –

0

In primo luogo, nel mio test, questa ha funzionato bene utilizzando una versione di Glassfish V3.

Ma, in secondo luogo, è probabile che si stia eseguendo un controllo di questa clausola delle specifiche Servlet 3.0.

I seguenti metodi vengono aggiunti al ServletContext dal Servlet 3.0 a consentire la definizione programmatica di servlet, filtri e il pattern URL che mappano. Questi metodi possono essere richiamati solo durante l'inizializzazione dell'applicazione dal metodo contexInitialized di un'implementazione ServletContextListener o dal metodo onStartup di un'implementazione ServletContainerInitializer.

In particolare, questi metodi NON possono essere chiamati da un metodo Filter.init(). Inizialmente l'ho provato con un metodo Servlet.init() e il metodo init non è riuscito perché il contesto era già inizializzato.

Quindi, il mio esperimento non ha duplicato il test esattamente - Non ho usato un metodo Filter.init() per questo, piuttosto ho inserito il codice in un ServletContextListener. E quando l'ho fatto, la mia annotazione @EJB è stata premiata.

Edit:

come non-utile come sembra, vorrei suggerire che questo è un bug in JBoss. Quando inizialmente ho provato il codice originale con l'iniezione dal filtro, Glassfish ha lanciato un'eccezione, poiché non ti è permesso fare l'iniezione, tranne dove ho menzionato prima. Ora forse questa è una "funzione aggiunta" di JBoss, ma apparentemente l'elaborazione di iniezione di @EJB semplicemente non funziona. Secondo le specifiche, questo dovrebbe funzionare come pubblicizzato.

+0

Grazie per l'uomo di risposta. Sfortunatamente non ha funzionato durante il caricamento attraverso ServletContextListener. Ho modificato la domanda sopra, potresti dirmi se ho fatto qualcosa di sbagliato. –

3

Servlet 3.0 Spec, Sez. 4.4.3.5

Resource iniezione [per esempio @EJB] su tutti i componenti (servlet, filtri e ascoltatori) aggiunti programmazione o creati programmazione, diversi da quelli aggiunti tramite i metodi che prende un'istanza, sarà supportato solo quando il componente è un gestito Bean. Per i dettagli su quello che è un Managed Bean fare riferimento alle specifiche Managed Bean definito come parte di Java EE 6 e JSR 299.

Managed Dichiarazione Bean

Un Java EE 6 bean gestito è annotata @javax.annotation.ManagedBean e ha un costruttore no-arg. Un bean gestito da JSR 299 (CDI) richiede semplicemente un costruttore no-arg o un costruttore annotato @javax.inject.Inject.

risposta

Per consentire l'iniezione di risorse è necessario:

  • posto @ManagedBean annotazione sul servlet aggiunto dinamicamente

    O

  • consentono CDI & includono una vuota beans.xml


Modifica

Anche se si sta creando il servlet in modo dinamico, è importante che il contenitore fa la creazione. Non pensare che la creazione all'interno di ServletContext supporterà l'iniezione. Servlet doc molto vago qui.

Con CDI prova:

servletContext.addServlet("your servlet name", @Inject YourServletClass servlet) 
+0

Ok ho provato l'annotazione '@ javax.annotation.ManagedBean' nella classe che estende HttpServlet (quello che viene mappato dinamicamente). Il mio fagiolo non è stato iniettato. Ho provato ad abilitare CDI inserendo un bean.wml vuoto nella directory META-INF. Ancora non è stato iniettato quando si utilizza @Inject. Qualche idea di cosa potrebbe essere? Farò diversi test con le informazioni che ho ottenuto finora. –

+1

Pensato che potrebbe accadere quando ServletContext crea il servlet. Forza il contenitore per creare il servlet per CDI, chiama servletContext.addServlet ("il tuo nome servlet", servlet @Inject YourServletClass) –

+0

Giusto per chiarire @GlenBest, "definito come parte di Java EE 6 e JSR 299". JSR 299 definisce Managed Beans, Java EE 6 definisce le altre classi che supportano l'injection, incluse le servlet (pagina 69 della specifica). Non c'è bisogno di fare un passo in più per rendere il Servlet un ManagedBean. –

Problemi correlati