Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Resource annotation doesn't work in Tomact 10.0.10

Recently I tried Tomcat 10.0.10 and when trying to inject the connection pool as a JNDI resource find out that the @Resource annotation doesn't work.

Then I tried obtain it programmatically by creating a InitialContext and it worked. Initially I thought it was only for the java:comp/env/jdbc so I tried with a simple bean like below and tried to inject it with the @Resource annotation it didn't work again. When I try to obtain it programmatically by creating a InitialContext and it works. Then I check whether the @PostConstruct or @PreDestroy annotation works and found out that they also don't work.

package lk.ijse.test.tomcatdbcp;
    
public class Something {
}
<?xml version="1.0" encoding="UTF-8"?>
<Context>

    <Resource name="bean/Something" auth="Container"
              type="lk.ijse.test.tomcatdbcp.Something"
              factory="org.apache.naming.factory.BeanFactory"
              />

</Context>
<?xml version="1.0" encoding="UTF-8"?>
<web-app metadata-complete="false" xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">

    <resource-env-ref>
        <resource-env-ref-name>bean/Something</resource-env-ref-name>
        <resource-env-ref-type>lk.ijse.test.tomcatdbcp.Something</resource-env-ref-type>
    </resource-env-ref>
</web-app>
package lk.ijse.test.tomcatdbcp;

import java.io.*;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;

import javax.naming.InitialContext;
import javax.naming.NamingException;

@WebServlet(name = "helloServlet", value = "/hello", loadOnStartup = 1)
public class HelloServlet extends HttpServlet {
    private String message;

    @Resource(name= "java:comp/env/bean/Something")
    private Something something;

    @PostConstruct
    public void doSomething(){
        System.out.println("Does it work?");
    }

    public void init() {
        message = "Hello World!";
        try {
            InitialContext ctx = new InitialContext();
            Something lookup = (Something) ctx.lookup("java:comp/env/bean/Something");
            System.out.println(lookup);
            System.out.println(something); // null
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");

        // Hello
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }

    public void destroy() {
    }
}

To reproduce the same issue, I created a sample repo here: https://github.com/sura-boy-playground/play-with-tomcat10 (Complete code can be found there)

At first, I had used javax.annotation.Resource annotation, so I thought that was the reason because of the javax.* to jakarta.* namespace change. Then I tried it with jakarta.annotation.Resource but the result was same.

I tried the same application with Tomcat 9.0.41 plus javax.* namespace, it works perfectly.

Is there any extra stuff that I need to do on Tomcat 10.0.10 to enable these annotations? I dug the Tomcat 10 documentation but I wasn't able to find out any thing related to my issue.

I found out that there was a similar case in Tomcat 7 previously, but I don't like that kind of workaround now. Tomcat @Resource annotations API annotation stops working in Tomcat 7

like image 824
Ranjith Suranga Avatar asked Oct 14 '22 19:10

Ranjith Suranga


1 Answers

You should declare the scope of your jakarta.annotation dependency as provided:

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.0.0</version>
    <scope>provided</scope>
</dependency>

If you have two copies of jakarta.annotation.Resource (one in the common classloader and one in your application's classloader), the two classes are different. The InstanceManager will look for fields annotated with the common classloader's copy of @Resource, while the something field is annotated with your webapp's copy of @Resource.

While adding jakarta.annotation with a scope different from provided is certainly a mistake, it is probably a common mistake and Tomcat has a fail-safe feature that ignores certain classes distributed with your application (cf. source code). Since jakarta.annotation.* classes are not among them, you can file a (wishlist) bug report.

Remark: You will have the same problem in Tomcat 9.0 if you use Java 11 or later. Before Java 11 the javax.annotation.* classes where included in the JRE. Servlet containers are required to look in the bootstrap/JRE classloader before looking in the webapp classloader (overriding javax.* classes is a breach of Java's licence), therefore Tomcat would never find the additional copy of the classes.

like image 179
Piotr P. Karwasz Avatar answered Oct 20 '22 16:10

Piotr P. Karwasz