Tomcat @Resource注释API注释在Tomcat 7中停止工作

时间:2012-05-30 10:34:53

标签: jsf tomcat annotations tomcat6 tomcat7

我使用Tomcat 6.0.26-6.0.35已经使用了JSF 2 Mojarra多年,各种版本高达2.1.2,我已经使用了几个月。我有几个请求范围和会话范围的bean,代码如下:

private @Resource(name="jdbc/cLabs", mappedName="jdbc/cLabs") DataSource cLabs;

已在我使用的每个Tomcat 6版本中正确注入。我也有其他类型的@Resource也不起作用,所以它不仅仅是DataSource resources.我尝试过切换到Tomcat 7.0.27而且突然间这些构造都不再有效。资源未注入。我还有其他类型的@Resource也不起作用,所以它不仅仅是DataSource资源。但是,在每种情况下,名为的资源确实存在,并且可以通过例如查找。

new InitialContext().lookup("java:comp/env/jdbc/cLabs");

[它们由context.xml中的元素定义]

这当然是皇家PITA,因为我花了一两年时间用前者替换后者。是否还有其他魔法咒语我必须与Tomcat 7编织才能让它再次运作?

请注意,资源 正确地注入到Servlet中,所以它并没有完全被破坏。 Tomcat和JSF之间的一些交互。

4 个答案:

答案 0 :(得分:2)

我的猜测是导致org.apache.catalina.core.DefaultInstanceManager忽略注释,因为faces要求@PostConstruct方法与@Resource字段分开处理。我创建了一个适用于使用@Resource注释的字段的解决方法。

将以下内容添加到web.xml:

<context-param>
    <param-name>com.sun.faces.injectionProvider</param-name>
    <param-value>com.example.faces.Tomcat7InjectionProvider</param-value>
</context-param>

并将该类添加到您的来源:

package com.example.faces;

import java.lang.reflect.Field;

import javax.annotation.Resource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;

import org.apache.catalina.util.Introspection;

import com.sun.faces.spi.InjectionProviderException;
import com.sun.faces.vendor.WebContainerInjectionProvider;

public class Tomcat7InjectionProvider extends WebContainerInjectionProvider {

    public Tomcat7InjectionProvider(ServletContext servletContext) {
    }

    @Override
    public void inject(Object managedBean) throws InjectionProviderException {
        if (managedBean != null) {
            // see org.apache.catalina.core.DefaultInstanceManager
            Field[] fields = Introspection.getDeclaredFields(managedBean.getClass());
            for (Field field : fields) {
                // field may be private
                field.setAccessible(true);
                if (field.isAnnotationPresent(Resource.class)) {
                    Resource annotation = null;
                    try {
                        annotation = field.getAnnotation(Resource.class);
                        Context ctx = new InitialContext();
                        Object resource = ctx.lookup("java:comp/env/" + annotation.name());
                        field.set(managedBean, resource);
                    } catch (Exception e) {
                        throw new InjectionProviderException("cannot find resource " + annotation.name(), e);
                    }
                }
            }
        }
    }
}

答案 1 :(得分:1)

回答我自己的问题,@ JeffE答案的改进版本。基本问题是:

  1. Tomcat6InjectionProvider随JSF 2.0提供,但在某些时候删除了。
  2. 默认WebContainerInjectionProvider不处理@Resource注释,正如JeffE指出的那样。
  3. 您可以在没有web.xml上下文条目的情况下解决此问题,如下所示:

    1. 创建一个名为META-INF/services/com.sun.faces.spi.injectionprovider的文件,并在其中添加以下行:

      com.sun.faces.vendor.Tomcat7InjectionProvider:org.apache.catalina.core.DefaultInstanceManager
      

      这一行的含义是,如果部署中存在第二个类,则第一个类用作注入提供者。上面的第二节是Tomcat 7的一部分。

    2. 编译以下课程。

    3. 此版本包含对JeffE版本的众多改进。具体做法是:

      • 根据@Resource@Resources Javadoc
      • 的要求处理超类
      • 它在班级级别处理@Resource@Resources注释
      • 根据@Resource Javadoc
      • 的要求,处理使用@Resource注释的方法
      • 根据name Javadoc
      • 的要求,正确处理@Resource@Resource个空的Field属性
      • 恢复package com.sun.faces.vendor; import com.sun.faces.spi.DiscoverableInjectionProvider; import com.sun.faces.spi.InjectionProviderException; import java.lang.reflect.Field; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Resource; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletContext; /** * @author Jeff E * @author Esmond Pitt Improvements named above. * * @see javax.annotation.Resource * * @see <a href="http://stackoverflow.com/a/21978577/207421">This StackOverflow * answer, although what org.apache.catalina.util.Introspection may be and where * it lives remains a mystery.</a> */ public class Tomcat7InjectionProvider extends DiscoverableInjectionProvider { private Logger logger = Logger.getLogger(this.getClass().getName()); private ServletContext servletContext; private WebContainerInjectionProvider delegate = new WebContainerInjectionProvider(); public Tomcat7InjectionProvider(ServletContext servletContext) { logger.config("constructed"); this.servletContext = servletContext; } @Override public void inject(Object managedBean) throws InjectionProviderException { logger.log(Level.CONFIG, "managedBean={0}", new Object[]{managedBean.getClass().getName()}); Class<?> clazz = managedBean.getClass(); do { List<Resource> classResources = new LinkedList<>(); // Process class-level @Resources and @Resource if (clazz.isAnnotationPresent(Resources.class)) { Resources annotation = clazz.getAnnotation(Resources.class); for (Resource resource : annotation.value()) { classResources.add(resource); } } if (clazz.isAnnotationPresent(Resource.class)) { Resource annotation = clazz.getAnnotation(Resource.class); classResources.add(annotation); } for (Resource annotation : classResources) { String name = annotation.name(); // Make sure the resource exists. try { Context ctx = new InitialContext(); Object resource = ctx.lookup("java:comp/env/" + name); } catch (NamingException exc) { throw new InjectionProviderException("checking class resource " + annotation.name()+" of "+clazz.getName(), exc); } } // Process fields with @Resource // see org.apache.catalina.core.DefaultInstanceManager // Field[] fields = Introspection.getDeclaredFields(managedBean.getClass()); Field[] fields = managedBean.getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Resource.class)) { Resource annotation = field.getAnnotation(Resource.class); String name = annotation.name(); logger.log(Level.CONFIG, "injecting @Resource(name=\"{2}\") into {0}.{1}", new Object[] { managedBean.getClass().getName(), field.getName(), name }); try { Context ctx = new InitialContext(); Object resource; if (name != null && name.length() > 0) { resource = ctx.lookup("java:comp/env/" + name); } else { resource = ctx.lookup(clazz.getName() + "/" + field.getName()); } // field may be private boolean accessibility = field.isAccessible(); try { field.setAccessible(true); field.set(managedBean, resource); } finally { field.setAccessible(accessibility); } } catch (NamingException | IllegalAccessException exc) { throw new InjectionProviderException("injecting resource " + annotation.name()+" into "+clazz.getName()+"."+field.getName(), exc); } } } // Process methods with @Resource for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(Resource.class) && method.getName().startsWith("set") && method.getName().length() > 3 && method.getReturnType() == void.class && method.getParameterTypes().length == 1) { // It's a setter with @Resource Resource annotation = method.getAnnotation(Resource.class); String name = annotation.name(); logger.log(Level.CONFIG, "injecting @Resource(name=\"{2}\") via {0}.{1}", new Object[] { managedBean.getClass().getName(), method.getName(), name }); try { Context ctx = new InitialContext(); Object resource; if (name != null && name.length() > 0) { resource = ctx.lookup("java:comp/env/" + name); } else { name = method.getName().substring(3); name = name.substring(0,1).toLowerCase()+name.substring(1); resource = ctx.lookup(clazz.getName() + "/" + name); } // method may be private boolean accessibility = method.isAccessible(); try { method.setAccessible(true); method.invoke(managedBean, resource); } finally { method.setAccessible(accessibility); } } catch (NamingException | IllegalAccessException | InvocationTargetException exc) { throw new InjectionProviderException("injecting resource " + annotation.name()+" via "+clazz.getName()+"."+method.getName(), exc); } } } } while ((clazz = clazz.getSuperclass()) != Object.class); } @Override public void invokePostConstruct(Object managedBean) throws InjectionProviderException { logger.log(Level.CONFIG, "managedBean={0}", new Object[]{managedBean}); delegate.invokePostConstruct(managedBean); } @Override public void invokePreDestroy(Object managedBean) throws InjectionProviderException { logger.log(Level.CONFIG, "managedBean={0}", new Object[]{managedBean}); delegate.invokePreDestroy(managedBean); } } 的原始访问权限
      • 它与Tomcat类没有依赖关系。

      如果更改其包名,请调整上面的包名称。

      {{1}}

      E&安培; OE

答案 2 :(得分:0)

另一种可能性,但我认为不太可能;你在web.xml或web-fragment.xml文件中使用metadata-complete="true"

定义的元数据完整: metadata-complete属性定义此部署描述符和其他相关部署  该模块的描述符(例如,Web服务描述符)是完整的,或者类文件是否可用  对于此模块并与此应用程序打包应检查指定的注释  部署信息。如果将metadata-complete设置为&#34; true&#34;,则部署工具必须忽略any  指定部署信息的注释,可能存在于该类文件中  应用。如果未指定metadata-complete或设置为&#34; false&#34;,则部署工具必须检查  注释应用程序的类文件,由规范指定。

示例web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>ResourceTest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
 </welcome-file-list>
</web-app>

答案 3 :(得分:-1)

抱歉,由于代表我无法发表评论,或者我会要求您更清楚地了解您所看到的错误/等。话虽如此,我已经用Tom完整版“1.6.0_51-b11-457”测试了Tomcat 7.0.27和Tomcat 7.0.41,并且能够使用@Resource。

context.xml中

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Resource name="jdbc/Sample" auth="Container"
      type="javax.sql.DataSource" username="nbuser" password="nbuser"
      driverClassName="org.apache.derby.jdbc.ClientDriver" 
      url="jdbc:derby://localhost/Sample"
      maxActive="8" maxIdle="4"/>
</Context>

ResourceTest.java

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

@WebServlet("/ResourceTest")
public class ResourceTest extends HttpServlet {
  private static final long serialVersionUID = 1L;
  @Resource(name="jdbc/Sample")
  private DataSource ds;


public ResourceTest() {
    // TODO Auto-generated constructor stub
}


protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    PrintWriter out = response.getWriter();
    out.println("<body>");


    try {
        //Context initCtx = new InitialContext();
        //Context envCtx = (Context) initCtx.lookup("java:comp/env");
        //DataSource ds = (DataSource) envCtx.lookup("jdbc/Sample");
        Connection conn = ds.getConnection();
        Statement s = conn.createStatement();
        s.execute("Select * From \"NBUSER\".\"Friends\"");
        ResultSet rs = s.getResultSet();
        while (rs.next()) {
            out.println(rs.getString("NAME") + " is my friend.");
        }
        conn.close();
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    out.println("</body>");

}

/**
 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
 *      response)
 */
protected void doPost(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
}

}