I have been using Tomcat 6.0.26-6.0.35 for several years with JSF 2 Mojarra, various versions up to 2.1.2 which I have been using for some months. I have several request-scoped
My guess is something is causing org.apache.catalina.core.DefaultInstanceManager to ignore annotations because faces requires the @PostConstruct method to be processed apart from @Resource fields. I have created a workaround that works for fields annotated with @Resource.
Add the following to web.xml:
<context-param>
<param-name>com.sun.faces.injectionProvider</param-name>
<param-value>com.example.faces.Tomcat7InjectionProvider</param-value>
</context-param>
And add the class to your source:
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);
}
}
}
}
}
}
Sorry I'm unable to comment due to rep or I would of asked for more clarification on what errors/etc you are seeing. That being said I have tested with both Tomcat 7.0.27 and Tomcat 7.0.41 with java full version "1.6.0_51-b11-457" and was able to use @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
}
}
Another possibility, yet I would think unlikely; is you are using metadata-complete="true"
in your web.xml or web-fragment.xml file(s).
Defined metadata-complete: The metadata-complete attribute defines whether this deployment descriptor and other related deployment descriptors for this module (e.g., web service descriptors) are complete, or whether the class files available to this module and packaged with this application should be examined for annotations that specify deployment information. If metadata-complete is set to "true", the deployment tool must ignore any annotations that specify deployment information, which might be present in the class files of the application. If metadata-complete is not specified or is set to "false", the deployment tool must examine the class files of the application for annotations, as specified by the specifications.
Example 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>
Answering my own question, an improved version of @JeffE's answer. The basic problem is:
Tomcat6InjectionProvider
was provided with JSF 2.0 but was removed at some point.WebContainerInjectionProvider
doesn't process @Resource annotations, as JeffE points out.You can overcome this without web.xml
context-entries as follows:
Create a file called META-INF/services/com.sun.faces.spi.injectionprovider
and add the following line to it:
com.sun.faces.vendor.Tomcat7InjectionProvider:org.apache.catalina.core.DefaultInstanceManager
The meaning of this line is that if the second class is present in the deployment, the first class is used as the injection provider. The second class above is part of Tomcat 7.
Compile the following class.
This version contains numerous improvements over JeffE's version. Specifically:
@Resource
and @Resources
Javadoc@Resource
and @Resources
annotations at the class level@Resource
, as required by the @Resource
Javadocname
attributes of @Resource
s correctly, as required by the @Resource
JavadocField
's original accessAdjust the package name above if you change its package name.
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