无法使用本地 Tomcat 服务器上的 Java REST-API 连接到 pgsql 数据库。堆栈包含 ExceptionInInitializerError 和 IllegalStateException

Unable to connect to pgsql database with Java REST-API on local Tomcat server. Stack contains ExceptionInInitializerError and IllegalStateException

我正在开发 REST-API,目前正试图解决一个问题。问题是当我在 chrome 中访问我的网页时,REST-API 无法连接到 pgsql 数据库。为了说明这个问题,我将 post 我的堆栈的精简版本并进行一些解释,描述所涉及的 Java classes 和 XML 的起源和功能。在wards 之后,我将展示一些涉及的XML 文件和java classes 的代码。我会寻找 ward 你对我的第一个 post.

的回答和反馈

UPDATE: at the end of the post I'll disclose my major progress towards finding the answer.

到目前为止,我一直在使用以下工具:

堆栈从这一行开始,这可能意味着 Jersey 运行 遇到了一些困难。

WARNING: The following warnings have been detected: WARNING: Unknown HK2 failure detected:

我的堆栈继续 java.lang.ExceptionInInitializerError,如下所示,源自我的数据库连接代理。

MultiException stack 1 of 2
java.lang.ExceptionInInitializerError
at nl.hu.v1wac.PostgresDAO.webservices.WorldResource.<init (WorldResource.java:25) 
Caused by: java.lang.RuntimeException: javax.naming.NameNotFoundException: Name [jdbc/PostgresDS] is not bound in this Context. Unable to find [jdbc].
at nl.hu.v1wac.PostgresDAO.jdbc.BaseDAO.<init>(BaseDAO.java:38)
at nl.hu.v1wac.PostgresDAO.jdbc.CountryDAO.<init>(CountryDAO.java:13)
at nl.hu.v1wac.PostgresDAO.model.WorldService.<init>(WorldService.java:9)
at nl.hu.v1wac.PostgresDAO.model.ServiceProvider.<clinit>(ServiceProvider.java:4)
... 61 more

多堆栈中的第二个异常是 IllegalStateException,源自我的 WorldResource class,包含统一资源标识符和 JSON 解释器,用于解析 json 消息到我的前端.

MultiException stack 2 of 2
java.lang.IllegalStateException: Unable to perform operation: create on nl.hu.v1wac.PostgresDAO.webservices.WorldResource
Caused by: java.lang.RuntimeException: javax.naming.NameNotFoundException: 
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:393)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

下面我会展示我的web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>MavenJersey</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>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>nl.hu.v1wac.PostgresDAO.webservices</param-value>
    </init-param>
    <init-param>
      <param-name>jersey.config.server.provider.classnames</param-name>
      <param-value>org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/restservices/*</url-pattern>
  </servlet-mapping>
</web-app>

下面我将展示我的BaseDAO。此 class 是我的数据访问对象的连接代理。

import java.sql.Connection;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.net.URI;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;

public class BaseDAO {
    private static final String URL = "jdbc:postgresql://";
    private static final String JDBC_DRIVER = "org.postgresql.Driver";
    private static final String CONTEXT = "java:comp/env/jdbc/PostgresDS";
    private DataSource connectionPool;
    public BaseDAO() {
    try {
        final String DATABASE_URL_PROP = System.getenv("DATABASE_URL");
        if (DATABASE_URL_PROP != null) {
            URI dbUri = new URI(DATABASE_URL_PROP);
            String dbUrl = URL + dbUri.getHost() + dbUri.getPath();
            BasicDataSource pool = new BasicDataSource();

            if (dbUri.getUserInfo() != null) {
                pool.setUsername(dbUri.getUserInfo().split(":")[0]);
                pool.setPassword(dbUri.getUserInfo().split(":")[1]);
                }
                pool.setDriverClassName(JDBC_DRIVER);
                pool.setUrl(dbUrl);
                pool.setInitialSize(1);

                connectionPool = pool;

        } else{
            InitialContext ic = new InitialContext();
            connectionPool = (DataSource) ic.lookup(CONTEXT);
        }
    } catch (Exception e) {

      throw new RuntimeException(e);
    }
    }
  protected final Connection getConnection() {
      try {
          return connectionPool.getConnection();
      } catch (Exception ex) {
          throw new RuntimeException(ex);
      }
  }
}

最后,我将用我的 WorldResource class 来结束这个 post。请注意,大部分代码已被排除。

public class WorldResource{
    private WorldService service = ServiceProvider.getWorldService();    
@GET
@RolesAllowed({"admin"})
@Path("/getAll")
@Produces(MediaType.APPLICATION_JSON)
public String getCountries() {
        JsonArrayBuilder jab;
        jab = Json.createArrayBuilder();
        for (Country c : service.getAllCountries()) {
            jab.add(makeCountryObj(c));
        }
        return jab.build().toString();
}

编辑: 在@Alok Sinha post 发现 context.xml 丢失后,我添加了我已经存在的 context.xml 来提供一些额外的洞察力。

<?xml version="1.0" encoding="UTF-8"?> 
<Context> <Resource name="jdbc/PostgresDS" 

url="jdbc:postgresql://localhost:5432/mydb" driverClassName="org.postgresql.Driver" 
auth="Container" 
type="javax.sql.DataSource" 
username="javaduser" 
password="javadude" 
/> 
<ResourceLink name="jdbc/PostgresDS" global="jdbc/PostgresDS" type="javax.sql.DataSource" 
/>
</Context>

更新:this manual of Apache 推荐的我的 web.xml 中添加资源引用后,我能够对我的连接问题有一些新的认识.

  <resource-ref>
    <description>PostgreSQL Datasource example</description>
    <res-ref-name>jdbc/PostgresDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

如下面的堆栈所示,添加资源引用似乎有助于将我的应用程序指向正确的方向。但是,名称和 url 似乎是空的。

Cannot create JDBC driver of class '' for connect URL 'null'
java.sql.SQLException: No suitable driver
at java.sql.DriverManager.getDriver(Unknown Source)
at nl.hu.v1wac.PostgresDAO.jdbc.BaseDAO.getConnection(BaseDAO.java:47)
at nl.hu.v1wac.PostgresDAO.webservices.WorldResource.getCountries(WorldResource.java:34)

更新2: 我试过另一种方法。我从一个初始的 Maven 项目开始,并从那里构建我的应用程序。我摆脱了 IllegalstateException。我完全按照 Alok Sinha 的描述实施了您的 context.xml。所以,没有资源链接。我还简化了 BaseDAO,使它只有一个 Initialcontext 和一个数据源。现在我可以在没有 basedao 的情况下将应用程序部署为 war 文件。如果我尝试使用 BaseDAO 部署它,由于 tomcat 日志中的以下错误,我无法 运行:

org.apache.catalina.deploy.NamingResourcesImpl.cleanUp Failed to retrieve
JNDI naming context for container [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/holidayboardapp]] so no cleanup was performed for that container 
  javax.naming.NameNotFoundException: Name [comp/env] is not bound in this Context. Unable to find [comp].`

您似乎没有在 tomcat 的 JNDI 中绑定数据源。您需要在 context.xml 中输入一个条目,如下所示 -

<Resource name="jdbc/PostgresDS" auth="Container" type="javax.sql.DataSource"
           maxActive="100" maxIdle="30" maxWait="10000"
           username="javauser" password="javadude" driverClassName="org.postgresql.Driver"
           url="jdbc:postgresql:localhost..."/>

与我原来的问题不同,解决方案取决于我如何部署我的应用程序。起点。下面我将描述我一路上学到的两个起点。

一个起点是使用 Eclipse EE 部署应用程序,然后 Eclipse 会将其部署到我的 Tomcat 根文件夹中的 wtpwebapps 上。 Eclipse 将您的应用程序部署在 wtpwebapps 文件夹中。然后,每个应用程序将作为 server.xml<Host> 的从属引用。这个答案也被提​​到了 on this Whosebug post.

<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">

另一个起点是将我的应用程序直接部署到 tomcat webapps 文件夹或使用 tomcat 管理器,我将在我的 maven 构建中包含 context.xml,就像 Alok辛哈正确地告诉了我。 Tomcat 然后会为我进行部署。

根据我个人的经验,您必须决定您更喜欢哪个起点。如果您想使用 Eclipse 来执行此操作,则不能使用 tomcat 管理器取消部署或重新部署应用程序,因为应用程序的上下文将硬编码在 server.xml 中。如果您仍想这样做,则必须从 server.xml 中手动删除应用程序的上下文。