我已经使用Neo4j OGM为Wildfly构建了RESTful Web服务,但是当我访问它时,我得到一个NullPointerException
。
似乎应该在我的模型类中填充的Map
在访问时尚未初始化。为什么会这样呢?
首先,我不得不说,该模块salessupport-ui
当前使用javafx作为胖客户端实现,并且可以很好地连接到neo4j社区版本2.2.4,可以正常阅读,书写。
我现在想做的就是我面临的问题,我想使用wildfly作为本身连接到neo4j的服务器,因此javafx客户端应用程序仅将请求发送到wildfly服务器。
我决定使用的协议是REST,在wildfly上已经提供的那种实现很容易。
以下是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
调试信息
通过调试,我可以看到它按预期的那样到达了类
SimpleRestGraphRepositoryImpl
中的第一个断点,方法findAll()
调用了repository.findAll();
同样也期望第二个,它进入方法
GraphRepositoryImpl
的内部findAll(...)
类和创建context.getSession().loadAll(clazz);
的行Session
。几步之后,它尝试在方法
org.neo4j.ogm.metadata.MetaData
中的_classInfo(String,String,String)
类中调用domainInfo.getClassInfosWithAnnotation(nodeEntityAnnotation);
其中nodeEntityAnnotation="org.neo4j.ogm.annotation.NodeEntity"
。在随后被调用的
DomainInfo
类方法getClassInfosWithAnnotation
中,映射annotationNameToClassInfo
为空。我希望它会被我的模型填充,这显然发生在javafx环境中,而不是在JavaEE env中发生。这是这种方法
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父级具有如下依赖管理定义:
<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-restsvc
pom.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();
}
}
通用简单休息图存储库界面
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);
}
...
}
附注:这是我关于stackoverflow的第一个问题,我希望我写尽可能少的文章来传达这个问题...
最佳答案
这是@NodeEntity中的Spring Data Neo4j吗?如果是,则必须正确设置Spring,在这里找不到。