无法使用本地 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.
到目前为止,我一直在使用以下工具:
- Eclipse EE(目前为 Oxygen II)
- 阿帕奇 Tomcat 8.5.14
- 行家
- Chrome
- PG-admin 4
堆栈从这一行开始,这可能意味着 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 中手动删除应用程序的上下文。
我正在开发 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.
到目前为止,我一直在使用以下工具:
- Eclipse EE(目前为 Oxygen II)
- 阿帕奇 Tomcat 8.5.14
- 行家
- Chrome
- PG-admin 4
堆栈从这一行开始,这可能意味着 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 中手动删除应用程序的上下文。