Я создал веб-службу RESTful для Wildfly, используя Neo4j OGM, но когда я обращаюсь к ней, я получаю ошибку NullPointerException
. Кажется, что Map
, который должен быть заполнен моими классами моделей, не был инициализирован к моменту доступа к нему. Почему это происходит?
Заранее я должен сказать, что этот модуль, salessupport-ui
, в настоящее время реализован как толстый клиент с использованием javafx, и он отлично работает, подключаясь к версии сообщества neo4j 2.2.4, чтение, запись, без проблем. Что я хотел бы сделать сейчас, и здесь я сталкиваюсь с проблемами, я хочу использовать wildfly в качестве сервера, который сам подключается к neo4j, таким образом, чтобы клиентское приложение javafx отправляло запросы только на сервер wildfly. Протокол, который я решил использовать, — это REST, реализация, которая уже есть на wildfly, проста.
Ниже приведены 1) исключение и некоторая информация об отладке, 2) подробности о моем контексте, структуре проекта и коде для моих классов.
1. Проблема
Вот исключение и мои выводы при отладке.
Исключение
Теперь, когда я вызываю эту службу REST, вводя http://localhost:8080/salessupport-restsvc/rest/address/list в браузере возникает следующее исключение:
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();
- 2-й также ожидается, он переходит во внутренний класс
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.
вот этот метод
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
Зависимости
Поясню зависимости:
У родителя службы поддержки продаж есть такое определение управления зависимостями:
<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();
}
}
Общий интерфейс репозитория Simple 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);
}
...
}
P.S.: Это мой первый вопрос по stackoverflow, надеюсь, я написал как можно меньше и как можно больше, чтобы донести проблему...
NullPointerException
, является частьюneo4j-ogm
? Если это так, то это кажется важным и, возможно, стоит подчеркнуть больше. - person jjaderberg   schedule 10.09.2015