我已经使用 Neo4j OGM 为 Wildfly 构建了一个 RESTful Web 服务,但是当我访问它时,我得到一个NullPointerException
. 似乎Map
应该用我的模型类填充的 a 在访问时尚未初始化。为什么会这样?
前面我不得不说,这个模块,salessupport-ui
目前是使用 javafx 作为胖客户端实现的,它可以很好地连接到 neo4j 社区版 2.2.4,阅读,写作,没问题。我现在想做什么,这就是我遇到问题的地方,我想使用wildfly作为本身连接到neo4j的服务器,因此javafx客户端应用程序只向wildfly服务器发送请求。我决定使用的协议是 REST,wildfly 已经提供的实现是 resteasy。
下面是 1) 异常和一些调试信息,2) 关于我的上下文、项目结构和我的类的代码的详细信息。
1.问题
这是调试时的异常和我的发现。
例外
现在,当我通过在浏览器中输入http://localhost:8080/salessupport-restsvc/rest/address/list来调用此 REST 服务时,会引发以下异常:
00:07:38,458 ERROR [io.undertow.request] (default task-5) UT005023: Exception handling request to /salessupport-restsvc/rest/address/list: org.jboss.resteasy.spi.UnhandledException: java.lang.NullPointerException
at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:76)
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:212)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:149)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:372)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:179)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:86)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:72)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:282)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:261)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:80)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:172)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:199)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:774)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at org.neo4j.ogm.metadata.MetaData.entityType(MetaData.java:231)
at org.neo4j.ogm.session.Neo4jSession.entityType(Neo4jSession.java:451)
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:55)
at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:94)
at org.neo4j.ogm.session.Neo4jSession.loadAll(Neo4jSession.java:114)
at groupid.salessupport.db.core.ApplicationContext$GraphRepositoryImpl.findAll(ApplicationContext.java:74)
at groupid.salessupport.restsvc.impl.SimpleRestGraphRepositoryImpl.findAll(SimpleRestGraphRepositoryImpl.java:29)
at groupid.salessupport.restsvc.impl.AddressRestImpl$Proxy$_$$_WeldClientProxy.findAll(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:137)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:296)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:250)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:237)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:356)
... 32 more
调试信息
- 通过调试,我可以看到它按预期到达了我在 class 中的第一个断点
SimpleRestGraphRepositoryImpl
,方法findAll()
调用repository.findAll();
- 第二个也是预期的,它进入内部
GraphRepositoryImpl
类的方法findAll(...)
和行context.getSession().loadAll(clazz);
,它创建了一个Session
. - 几步之后,它会尝试在
org.neo4j.ogm.metadata.MetaData
类中_classInfo(String,String,String)
调用domainInfo.getClassInfosWithAnnotation(nodeEntityAnnotation);
where的方法nodeEntityAnnotation="org.neo4j.ogm.annotation.NodeEntity"
。 - 在随后调用的
DomainInfo
类方法中,映射为空。我希望它充满我的模型,这显然发生在 javafx 环境中,而不是在 JavaEE 环境中。getClassInfosWithAnnotation
annotationNameToClassInfo
这是那个方法
public List<ClassInfo> getClassInfosWithAnnotation(String annotation) {
return annotationNameToClassInfo.get(annotation);
}
为了检查,如果 REST 机制按原样工作,我在 AddressRestImpl> 旁边放了一个类
@Path("dummy")
public class DummyImpl {
@GET
@Path("list")
@Produces(MediaType.APPLICATION_JSON)
public List<Amount> getAll() {
return Arrays.asList(new Amount());
}
}
通过导航到http://localhost:8080/salessupport-restsvc/rest/dummy/list在浏览器中调用它会产生预期的结果:[{"amount":null,"currency":null}]
我在这里有很多简单的东西需要改进,我尝试了很多方法,在 Tomcat 8 上也发生了同样的异常,但是即使在虚拟情况下,Rest 方法也不起作用,所以我切换到了 wildfly。我在这里发现了一个错误,还是我在设置中遗漏了一些东西?
2) 项目和代码细节
这是有关我的环境、项目结构和遇到此问题时运行的代码的信息。
环境
我的环境如下:
我有 Neo4J 社区版 2.2.4。
我下载了 wildfly-9.0.1.Final,除了一个与我的 NVidia 驱动程序软件冲突的管理端口外,我没有改变所有内容。
项目结构
我有一个 Maven 多模块应用程序,它由以下模块组成:
<modules>
<module>salessupport-ui</module>
<module>salessupport-intf</module>
<module>salessupport-db</module>
<module>salessupport-restsvc</module>
</modules>
依赖关系如下:
salessupport-intf <-- salessupport-db
^
|-- salessupport-ui
|-- salessupport-restsvc
依赖项
让我解释一下依赖关系:
salessupport parent 有一个这样的依赖管理定义:
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm</artifactId>
<version>1.1.2</version>
<exclusions>
<exclusion>
<groupId>org.neo4j.app</groupId>
<artifactId>neo4j-server</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
salessupport-intf 使用以下依赖项:
<dependencies>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
</dependencies>
salessupport-restsvcpom.xml
是
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>groupid</groupId>
<artifactId>salessupport</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>salessupport-restsvc</artifactId>
<name>salessupport-restsvc</name>
<url>http://maven.apache.org</url>
<properties>
<jersey.version>1.19</jersey.version>
<resteasy.version>3.0.11.Final</resteasy.version>
</properties>
<dependencies>
<dependency>
<groupId>groupid</groupId>
<artifactId>salessupport-intf</artifactId>
</dependency>
<dependency>
<groupId>groupid</groupId>
<artifactId>salessupport-db</artifactId>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>${resteasy.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<packaging>war</packaging>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${version.war.plugin}</version>
<configuration>
<!-- webXml>src\main\webapp\WEB-INF\web.xml</webXml -->
<failOnMissingWebXml>false</failOnMissingWebXml>
<packagingExcludes>
org.neo4j.server.rest.discovery,
org.neo4j.server.rest.web
</packagingExcludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
代码
这是相关的课程。
GraphRepository
界面
我定义了我的自定义 GraphRepository 接口:
public interface GraphRepository<S> {
Iterable<S> findAll(Iterable<Long> nodeIds);
Iterable<S> findAll();
void delete(S arg0);
S save(S arg0);
}
模型类
有一些模型类,例如:
package groupid.salessupport.db.model;
import java.text.DecimalFormat;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.NodeEntity;
@NodeEntity(label="Amount")
public class Amount {
@GraphId Long id;
private Double amount;
private Currency currency;
public Currency getCurrency() {
return currency;
}
public void setCurrency(Currency currency) {
this.currency = currency;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
@Override
public String toString() {
return new DecimalFormat().format(amount)+" "+currency;
}
}
和
@NodeEntity(label="Currency")
public class Currency extends BaseObject {
@Override
public String toString() {
return getName();
}
}
通用简单 Rest Graph Repository 接口
public interface ISimpleRestGraphRepository<T> {
@DELETE
@Path("delete")
@Consumes(MediaType.APPLICATION_JSON)
public void delete(T arg0);
@PUT
@Path("save")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public T save(T arg0);
@GET
@Path("list")
@Produces(MediaType.APPLICATION_JSON)
public Iterable<T> findAll();
@POST
@Path("listbyids")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Iterable<T> findAll(Iterable<Long> arg0);
}
金额的 REST 接口定义
@Path("amount")
public interface AmountRest extends ISimpleRestGraphRepository<Amount> {
}
REST 激活器
@ApplicationPath("/rest")
public class JaxRsActivator extends Application {
}
我的 REST 服务的通用实现
@RequestScoped
public class SimpleRestGraphRepositoryImpl<T> implements ISimpleRestGraphRepository<T> {
private final GraphRepository<T> repository;
public SimpleRestGraphRepositoryImpl(GraphRepository<T> repository) {
this.repository = repository;
}
@Override
public void delete(T arg0) {
repository.delete(arg0);
}
@Override
public T save(T arg0) {
return repository.save(arg0);
}
@Override
public Iterable<T> findAll() {
return repository.findAll();
}
@Override
public Iterable<T> findAll(Iterable<Long> arg0) {
return repository.findAll(arg0);
}
}
...以及地址的具体实现:
public class AmountRestImpl extends SimpleRestGraphRepositoryImpl<Amount> implements AmountRest {
public AmountRestImpl() {
super(NeoRepositories.getInstance().getAmountRepository());
}
}
数据库连接
在 salessupport-db 中,我们使用一些代码连接到数据库:
public class ApplicationContext {
private SessionFactory sessionFactory;
private Session openSession;
public ApplicationContext() {
}
public SessionFactory getSessionFactory() {
if (sessionFactory == null) {
sessionFactory = new SessionFactory("groupid.salessupport.db.model");
}
return sessionFactory;
}
public Session getSession() {
if (openSession == null) {
openSession = getSessionFactory().openSession("http://localhost:7474",
"username", "password"); // it is not really like this
}
return openSession;
}
public <S,G extends GraphRepository<S>> GraphRepository<S> getGraphRepository(Class<S> clazz) {
return new GraphRepositoryImpl<S>(this, clazz);
}
public static class GraphRepositoryImpl<S> implements GraphRepository<S> {
private ApplicationContext context;
private Class<S> clazz;
public GraphRepositoryImpl(ApplicationContext context, Class<S> clazz) {
this.context = context;
this.clazz = clazz;
}
@Override
public Iterable<S> findAll(Iterable<Long> nodeIds) {
List<Long> listNodeIds;
if (nodeIds instanceof List) {
listNodeIds = (List<Long>) nodeIds;
} else {
listNodeIds = new LinkedList<>();
for (Long l : nodeIds) {
listNodeIds.add(l);
}
}
return context.getSession().loadAll(clazz,listNodeIds);
}
@Override
public Iterable<S> findAll() {
return context.getSession().loadAll(clazz);
}
@Override
public void delete(S arg0) {
Session session = context.getSession();
Transaction transaction = session.beginTransaction();
context.getSession().delete(arg0);
transaction.commit();
transaction.close();
}
@Override
public S save(S arg0) {
Session session = context.getSession();
Transaction transaction = session.beginTransaction();
session.save(arg0);
transaction.commit();
transaction.close();
return arg0;
}
}
}
用法
实现者通过“单例”获取这个GraphRepository
又快又脏的实例:
public class NeoRepositories {
private ApplicationContext context;
private static final NeoRepositories INSTANCE = new NeoRepositories();
private NeoRepositories() {
context = new ApplicationContext();
}
public GraphRepository<Person> getPersonRepository() {
return context.getGraphRepository(Person.class);
}
public static NeoRepositories getInstance() {
return INSTANCE;
}
public GraphRepository<Amount> getAmountRepository() {
return context.getGraphRepository(Amount.class);
}
...
}
PS:这是我关于stackoverflow的第一个问题,我希望我写的尽可能少,尽可能多地传达问题......