2017-08-16 3 views
2

Je cherche à utiliser l'injection "@Resource String ..." disponible dans les conteneurs servlet 3.0+ pour fournir facilement des paramètres de configuration aux servlets. Je voudrais que les valeurs par défaut fonctionnent et échouent si la clé n'est pas présente dans JNDI (indiquant une erreur de configuration)Erreur @Resource: "Naming binding existe déjà pour foo.NewServlet/userName dans l'espace de noms"

J'ai joué avec une simple servlet dans Netbeans 8.2 avec Glassfish 4.1.1 où je voudrais avoir le champ userName et le setFullName(String fullName) ensemble:

package foo; 

import java.io.IOException; 
import java.io.PrintWriter; 
import javax.annotation.Resource; 
import javax.servlet.ServletException; 
import javax.servlet.annotation.WebServlet; 
import javax.servlet.annotation.WebInitParam; 
import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

@WebServlet(name = "NewServlet", urlPatterns = {"/NewServlet"}) 
public class NewServlet extends HttpServlet { 

    @Resource(description="user name") 
    String userName; 

    private String fullname; 

    @Resource() 
    public void setFullName(String fullName){ 
     this.fullname = fullName; 
    } 

    protected void processRequest(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException { 
     response.setContentType("text/html;charset=UTF-8"); 
     try (PrintWriter out = response.getWriter()) { 
      /* TODO output your page here. You may use following sample code. */ 
      out.println("<!DOCTYPE html>"); 
      out.println("<html>"); 
      out.println("<head>"); 
      out.println("<title>Servlet NewServlet</title>");    
      out.println("</head>"); 
      out.println("<body>"); 
      out.println("Full name = " + fullname); 
      out.println("<h1>Servlet NewServlet at " + request.getContextPath() + "</h1>"); 
      out.println("Username = " + userName); 
      out.println("</body>"); 
      out.println("</html>"); 


     } 
    } 
// Autogenerated stuff omitted 
} 

sans « web.xml » les champs sont juste nuls (et pas d'échec). J'ai ensuite joué avec "web.xml" pour voir comment je pourrais le définir. Le nom "java: comp/env/foo: NewServlet/fullName" est ce que Glassfish 4.1.1 semble créer comme nom pour le setter fullName.

<?xml version="1.0" encoding="UTF-8"?> 
<web-app xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
    version="3.0"> 
    <env-entry > 
     <env-entry-name>java:comp/env/foo.NewServlet/fullName</env-entry-name> 
     <env-entry-type>java.lang.String</env-entry-type> 
     <env-entry-value>!BAR!</env-entry-value> 
    </env-entry> 
    <env-entry > 
     <env-entry-name>java:comp/env/foo.NewServlet/userName</env-entry-name> 
     <env-entry-type>java.lang.String</env-entry-type> 
     <env-entry-value>!USERNAME!</env-entry-value> 
    </env-entry> 
</web-app> 

Cela échoue avec

Severe: Exception while deploying the app [WebApplication4] : Naming binding already exists for foo.NewServlet/userName in namespace {java:module/env/foo.NewServlet/userName=Env-Prop: java:comp/env/foo.NewServlet/[email protected] [email protected]@[email protected]@, java:module/env/foo.NewServlet/fullName=Env-Prop: java:comp/env/foo.NewServlet/[email protected] [email protected]@[email protected]@} 

Il n'y a rien d'autre que ces deux fichiers dans le projet. Apparemment, je me méprends sur quelque chose de fondamental, mais lire le didacticiel Java EE et rechercher des suggestions ne m'a pas aidé. Je voudrais vraiment deux choses:

  1. Ou ne pas fournir des conseils à la ressource balise @ en utilisant les paramètres par défaut générés par conteneur ou tout simplement une clé comme « our.application.fullName ».
  2. Echoue bruyamment si quelque chose ne va pas, y compris la valeur-clé non présente.

Suggestions? Une bonne réponse donnera une prime de 500 points.

+0

Une question distincte s'est ouverte sur le comportement de Wildfly/Glassfish - https://stackoverflow.com/q/45716813/53897 –

Répondre

1

Vous avez vraiment deux détails importants: seulement manqué

  1. Lorsque vous spécifiez le nom d'une ressource dans un descripteur de déploiement (comme le web.xml), que ce soit un env-entry-name, resource-env-ref-name ou ejb-ref-name, etc, la partie java:comp/env du nom JNDI est toujours implicite. Par conséquent, si vous voulez une ressource définie par un env-entry apparaisse dans JNDI à java:comp/env/foo, vous spécifiez son env-entry-name comme:

     <env-entry-name>foo</env-entry-name> 
    
  2. La spécification Java EE (de §EE.5.2.5) modifie les règles de nom « par défaut » appliqué à @Resource annotations:

    A field of a class may be the target of injection. The field must not be final. By default, the name of the field is combined with the fully qualified name of the class and used directly as the name in the application component’s naming context. For example, a field named myDatabase in the class MyApp in the package com.example would correspond to the JNDI name java:comp/env/ com.example.MyApp/myDatabase. The annotation also allows the JNDI name to be specified explicitly. When a deployment descriptor entry is used to specify injection, the JNDI name and the field name are both specified explicitly. Note that, by default, the JNDI name is relative to the java:comp/env naming context.

    en d'autres termes, si le nom complet de votre servlet est com.p45709634.NewServlet, le nom JNDI du champ userName va être java:comp/env/com.p45709634.NewServlet/userName. Par conséquent, son env-entry-name va être:

     <env-entry-name>com.p45709634.NewServlet/userName</env-entry-name> 
    

Donc, si vous utilisez ces noms qualifiés dans votre site Web.fichier xml, vous pouvez heureusement déclarer les champs annotés comme vous l'avez:

@Resource 
    private String userName; 

    private String fullname; 

    @Resource 
    public void setFullName(String fullName){ 
     this.fullname = fullName; 
    } 

Maintenant ce même chapitre des états de spécification:

If the container fails to find a resource needed for injection, initialization of the class must fail, and the class must not be put into service.

Toutefois, cela ne semble pas se produire dans la pratique (au moins sur GlassFish pour vous et WildFly pour moi). Cela peut être dû à un report de la spécification CDI pour l'injection, ce qui ne semble pas avoir grand chose à dire au sujet de l'impossibilité de localiser les ressources pour l'injection. Par conséquent, nous pouvons être bloqués avec la validation de ces champs dans la méthode init ou une méthode annotée @PostConstruct.

+0

J'ai supprimé la partie "java:/comp/env" des balises env-entry-name, et cela a fonctionné ! Je vous remercie! Je suppose que le message d'erreur était sur "/ comp/env" déjà lié, pas la clé réelle déjà utilisée. Je me demande si c'est un bug connu ou une caractéristique de Wildfly/Glassfish et si elle peut être appliquée en utilisant un interrupteur? Quoi qu'il en soit, je vais vous donner la prime quand je peux. Rappelez-moi si j'oublie. –