一尘不染

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

tomcat

几年来,我一直在JSF 2 Mojarra中使用Tomcat
6.0.26-6.0.35,直到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之间的一些交互。


阅读 218

收藏
2020-06-16

共1个答案

一尘不染

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

  1. Tomcat6InjectionProviderJSF 2.0提供了A ,但在某个时候将其删除。
  2. WebContainerInjectionProvider正如JeffE指出的那样,默认值不处理@Resource批注。

您可以在没有web.xml上下文输入的情况下克服此问题,如下所示:

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

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

  1. 编译以下类。

该版本比JeffE的版本包含许多改进。特别:

  • 它根据@Resource@ResourcesJavadoc的要求处理超类
  • 它在类级别处理@Resource@Resources注释
  • @Resource根据@ResourceJavadoc的要求处理带有注释的方法
  • 它按照Javadoc的要求正确处理s的空name属性或缺失属性@Resource``@Resource
  • 还原Field的原始访问权限
  • 它与Tomcat类无关。

如果更改包名称,请在上方调整包名称。

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

E&OE

2020-06-16