GWT RequestFactory: NullPointerException при работе с существующей сущностью - PullRequest
0 голосов
/ 28 февраля 2012

Я пытаюсь интегрировать GWT и Hibernate , используя RequestFactory . Все работает хорошо, кроме случаев, когда я пытаюсь работать с существующей сущностью. Неважно, что я пытаюсь сделать (загрузить, обновить или удалить), результат всегда один и тот же: NullPointerException .

После некоторой отладки я понял, что при запуске запроса JsonSplittable , который содержит только строку с идентификатором моей существующей сущности (например, "1") обрабатывается так, как если бы он имел JSONObject . Метод getOrReify выполняется и происходит NPE.

getOrReify , похоже, ищет операцию для выполнения, поскольку ее поиск - "O" ( propertyName ). Но, как я уже сказал, он не найдет его, потому что нет JSONObject. Кроме того, reifiedMap , которая может предоставить эту информацию, не имеет ее (но она не равна нулю).

Исключение

28/02/2012 11:10:51 com.google.web.bindery.requestfactory.server.RequestFactoryServlet doPost
GRAVE: Unexpected error
java.lang.NullPointerException
    at com.google.web.bindery.autobean.vm.impl.JsonSplittable.isNull(JsonSplittable.java:248)
    at com.google.web.bindery.autobean.shared.impl.AbstractAutoBean.getOrReify(AbstractAutoBean.java:235)
    at com.google.web.bindery.autobean.vm.impl.ProxyAutoBean.getOrReify(ProxyAutoBean.java:229)
    at com.google.web.bindery.autobean.vm.impl.BeanMethod$2.invoke(BeanMethod.java:73)
    at com.google.web.bindery.autobean.vm.impl.SimpleBeanHandler.invoke(SimpleBeanHandler.java:43)
    at $Proxy81.getOperations(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor67.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.google.web.bindery.autobean.vm.impl.ShimHandler.invoke(ShimHandler.java:78)
    at $Proxy81.getOperations(Unknown Source)
    at com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.processOperationMessages(SimpleRequestProcessor.java:496)
    at com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.decodeOobMessage(SimpleRequestProcessor.java:185)
    at com.google.web.bindery.requestfactory.server.RequestState.getBeansForIds(RequestState.java:254)
    at com.google.web.bindery.requestfactory.server.RequestState.getBeansForPayload(RequestState.java:147)
    at com.google.web.bindery.requestfactory.server.RequestState.getBeanForPayload(RequestState.java:124)
    at com.google.web.bindery.requestfactory.shared.impl.EntityCodex.decode(EntityCodex.java:101)
    at com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.decodeInvocationArguments(SimpleRequestProcessor.java:409)
    at com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.decodeInvocationArguments(SimpleRequestProcessor.java:380)
    at com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.processInvocationMessages(SimpleRequestProcessor.java:447)
    at com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.process(SimpleRequestProcessor.java:225)
    at com.google.web.bindery.requestfactory.server.SimpleRequestProcessor.process(SimpleRequestProcessor.java:127)
    at com.google.web.bindery.requestfactory.server.RequestFactoryServlet.doPost(RequestFactoryServlet.java:133)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at com.google.inject.servlet.ServletDefinition.doService(ServletDefinition.java:263)
    at com.google.inject.servlet.ServletDefinition.service(ServletDefinition.java:178)
    at com.google.inject.servlet.ManagedServletPipeline.service(ManagedServletPipeline.java:91)
    at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:62)
    at my.package.persistence.filter.HibernateSessionRequestFilter.doFilter(HibernateSessionRequestFilter.java:159)
    at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163)
    at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
    at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:168)
    at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
    at my.package.persistence.filter.TenantFilter.doFilter(TenantFilter.java:171)
    at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163)
    at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
    at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:168)
    at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
    at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:118)
    at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:113)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1088)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:324)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:647)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)

Entity

@Entity
public class Foro implements RequestFactoryEntity<Long> {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(length = 100)
    private String descricao;

    @Column(name = Constants.VERSION_COLUMN)
    @Version
    private Integer version;

    public Foro() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getDescricao() {
        return descricao;
    }

    public void setDescricao(String descricao) {
        this.descricao = descricao;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }
}

Proxy

@ProxyFor(value = Foro.class, locator = Locators.Foro.class)
public interface ForoProxy extends EntityProxy {

    EntityProxyId<ForoProxy> stableId();

    String getDescricao();

    void setDescricao(String descricao);

}

Локатор (упрощенный с суперклассом)

// Locator
public final class Locators {

    private Locators() {
    }

    public static final class Foro extends AbstractDaoLocator<ForoDao, my.package.evolution.core.entity.Foro, Long> {
    }

}

// Locator superclass
public abstract class AbstractDaoLocator<DAO extends AbstractDao<T, ID>, T extends RequestFactoryEntity<ID>, ID extends Serializable> extends AbstractLocator<T, ID> {

    @Override
    public T find(Class<? extends T> clazz, ID id) {
        try {
            return getDaoClass().newInstance().findById(id);

        } catch (Exception e) {
            throw new RuntimeException("Falha ao instanciar DAO: " + getDaoClass().getName() + ". Este DAO tem um construtor vazio?", e);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Class<T> getDomainType() {
        return (Class<T>) getTypes()[1];
    }

    @Override
    @SuppressWarnings("unchecked")
    public Class<ID> getIdType() {
        return (Class<ID>) getTypes()[2];
    }

    @SuppressWarnings("unchecked")
    private Class<DAO> getDaoClass() {
        return (Class<DAO>) getTypes()[0];
    }

    private Type[] getTypes() {
        return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
    }

}

// AbstractDaoLocator superclass
public abstract class AbstractLocator<T extends RequestFactoryEntity<ID>, ID extends Serializable> extends Locator<T, ID> {

    @Override
    public T create(Class<? extends T> clazz) {
        try {
            return clazz.newInstance();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Class<T> getDomainType() {
        return (Class<T>) getTypes()[0];
    }

    @Override
    public ID getId(T domainObject) {
        return domainObject.getId();
    }

    @Override
    @SuppressWarnings("unchecked")
    public Class<ID> getIdType() {
        return (Class<ID>) getTypes()[1];
    }

    @Override
    public Object getVersion(T domainObject) {
        return domainObject.getVersion();
    }

    private Type[] getTypes() {
        return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
    }

}

ServiceLocator (упрощенный с суперклассом)

// ServiceLocator
public final class ServiceLocators {

    private ServiceLocators() {
    }

    public static final class ForoDao extends AbstractServiceLocator<br.com.programarte.evolution.core.dao.ForoDao> {
    }

}

// ServiceLocator superclass
public abstract class AbstractServiceLocator<T> implements ServiceLocator {

    public Object getInstance(Class<?> clazz) {
        try {
            return getServiceClass().newInstance();

        } catch (Exception e) {
            throw new RuntimeException("Falha ao instanciar a classe de servi\u00e7o: " + getServiceClass().getName() + ". Ela tem um construtor vazio?", e);
        }
    }

    @SuppressWarnings("unchecked")
    private Class<T> getServiceClass() {
        return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

}

DAO (суперкласс AbstractDao из книги Hibernate)

public class ForoDao extends AbstractDao<Foro, Long>{
}

RequestFactory

public interface RequestContextFactory extends RequestFactory {

    ForoRequestContext foroRequestContext();

    @Service(value = ForoDao.class, locator = ServiceLocators.ForoDao.class)
    public interface ForoRequestContext extends RequestContext {

        Request<ForoProxy> findById(Long id);

        Request<ForoProxy> saveOrUpdate(ForoProxy proxy);

        Request<Void> delete(ForoProxy proxy);

    }
* * Выполнение тысяча сорок-девять
getFactory().foroRequestContext().findById(3L).fire(new SimpleReceiver<ForoProxy>() {

    @Override
    public void onSuccess(ForoProxy entity) {
        final ForoRequestContext ctx = getFactory().foroRequestContext();
        final ForoProxy entityEdit = ctx.edit(entity);
        entityEdit.setDescricao(entityEdit.getDescricao() + " (Edit)");

        ctx.saveOrUpdate(entityEdit).fire(new SimpleReceiver<ForoProxy>());
    }

});

Больше информации (запрошено Колином Алвортом)

Create / Setup RequestFactory

private RequestContextFactory rcf;

private RequestContextFactory getFactory() {
    if (null == rcf) {
        rcf = new SmartContextFactory().getRequestContextFactory();
    }

    return rcf;
}

SmartContextFactory ###

public class SmartContextFactory {

    private final RequestContextFactory rcf = GWT.create(RequestContextFactory.class);
    private final EventBus eventBus = new SimpleEventBus();

    public SmartContextFactory() {
        rcf.initialize(eventBus);
    }

    public RequestContextFactory getRequestContextFactory() {
        return rcf;
    }

    public EventBus getEventBus() {
        return eventBus;
    }

}

jsonRequestString (украшено)

{
    "F": "my.package.evolution.services.requestfactory.RequestContextFactory",
    "O": [{
        "T": "cv432aQBAMWN$1T23hbccUQX5WY=",
        "V": "MS4w",
        "P": {
            "descricao": "Nowhere (1330517239646) (Edit)"
        },
        "S": "IjI3Ig==",
        "O": "UPDATE"
    }],
    "I": [{
        "P": [{
            "T": "cv432aQBAMWN$1T23hbccUQX5WY=",
            "S": "IjI3Ig=="
        }],
        "O": "r6H_7BrJ_7Y_j0lWkuJA$zEJ6mc="
    }]
}

Ответы [ 2 ]

0 голосов
/ 29 февраля 2012

Переменная «O» - это операции, отправляемые по проводам, объявленные в com.google.web.bindery.requestfactory.shared.messages.RequestMessage. Операция описана в com.google.web.bindery.requestfactory.shared.messages.OperationMessage - она ​​представляет набор или операции, выполняемые над объектами до их сохранения (в отличие от com.google.web.bindery.requestfactory.shared.messages.InvocationMessage, который представляет операции службы).

Чтобы этот нулевой указатель имел место, весь RequestMessage, по-видимому, должен быть нулевым - не было объекта для чтения свойства "O" - JsonSplittable был каким-то образом создан без базовых данных.

Я не вижу способа, который может случиться при трассировке кода, вызываемого для десериализации сообщения от клиента в экземпляр RequestMessage, за одним исключением - если вместо объекта отправляется строка JSON. Это не имеет смысла, по крайней мере с любой настройкой по умолчанию.


Из обновленных сообщений выясняется, что JsonSplittable был создан, оборачивая строку вместо объекта Json, что дает нам NPE. Из этого мы только знаем, что некоторая строка десериализуется, но сервер считает, что это должен быть объект, и поэтому пытается (и не может) прочитать подвойства. Теперь попытка выяснить, какое свойство мы читаем, оказывается трудной только из трассировки стека, поскольку это может быть чтение любого вспомогательного свойства, чтобы подготовить его к доступу в виде компонента. Свойство id было вероятным кандидатом, так как его следует отправлять как String-> Long, но если сервер ожидал какого-то другого типа, он попытался бы десериализовать его как jsonobject-> object.

То, что другие операции, которые не отправляют объект на сервер, работают правильно, похоже, подтверждает это. Я все еще подозреваю, что здесь что-то идет не так (или что трассировка стека изменилась, или свойство, в котором он застрял, изменилось) - выяснение того, какое свойство он пытается прочитать в данный момент, будет важно для решения этой проблемы.

java.lang.NullPointerException
    at com.google.web.bindery.autobean.vm.impl.JsonSplittable.isNull(JsonSplittable.java:248)
    at com.google.web.bindery.autobean.shared.impl.AbstractAutoBean.getOrReify(AbstractAutoBean.java:235)
    at com.google.web.bindery.autobean.vm.impl.ProxyAutoBean.getOrReify(ProxyAutoBean.java:229)
    at com.google.web.bindery.autobean.vm.impl.BeanMethod$2.invoke(BeanMethod.java:73)

Последний кадр здесь, где фактически вызывается геттер, попробуйте разбить исключение, чтобы прочитать некоторые значения и узнать несколько вещей:

  • это все еще происходит из getOperations, и свойство все еще "O", которое вызывается в строке? При условии, что
  • Какая строка в JsonSplittable обрабатывается как объект? Со строкой запроса это не имеет смысла быть чем-то иным, как единственным объектом в массиве O (или строкой «UPDATE» -> enum).

На некоторые другие вопросы, которые я задавал, были даны ответы

Можете ли вы опубликовать тело сообщения, которое отправляется по сети? Это должно быть доступно как jsonRequestString в com.google.web.bindery.requestfactory.server.RequestFactoryServlet.doPost(HttpServletRequest, HttpServletResponse), который находится в вашей трассировке стека, или просматривать текст с помощью инструмента, такого как Firebug или Chrome Inspector. Можете ли вы также поделиться, как вы создаете / настраиваете экземпляр RequestFactory? Если вы использовали пользовательский транспорт, возможно (хотя это кажется абсурдным), что данные могут быть отправлены как-то неправильно.

И, наконец, работают ли радиочастотные вызовы? Если все они терпят неудачу, это также указывает на проблему конфигурации некоторого вида. Если это единственное сообщение, возможно, в сообщении будет что-то, что проливает свет на проблему.

0 голосов
/ 28 февраля 2012

Я не уверен, что вам нужен метод stableId () в вашем прокси. Я думаю, что это генерируется для вас на стороне клиента. Не уверен, что это ваша проблема, но стоит попробовать без этого в вашем прокси-интерфейсе.

...