Ошибка GWT RequestFactory + ServiceLocator

Я использую GWT 2.2 с RequestFactory. Приложение имеет существующий сервисный уровень (на стороне сервера), поэтому я использую ServiceLocator для предоставления этих реализаций. My Proxy и RequestContexts указывают правильную службу и локатор для использования (как показано здесь< /а>). Я могу делать базовые запросы данных, но когда я пытаюсь сохранить, я получаю следующее исключение:

com.google.gwt.requestfactory.server.UnexpectedException: Could not instantiate Locator com.schedgy.core.service.OrganizationService. Is it default-instantiable?
at com.google.gwt.requestfactory.server.ServiceLayerDecorator.die(ServiceLayerDecorator.java:185)
at com.google.gwt.requestfactory.server.LocatorServiceLayer.newInstance(LocatorServiceLayer.java:222)
at com.google.gwt.requestfactory.server.LocatorServiceLayer.createLocator(LocatorServiceLayer.java:47)
at com.google.gwt.requestfactory.server.ServiceLayerDecorator.createLocator(ServiceLayerDecorator.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

OrganizationService определяется как:

// Parent class provides the generic Locator<Organization, String> methods
public class OrganizationService extends CompanyEntityService<Organization> {

        protected OrganizationDao organizationDao;

        protected UserDao userDao;

        protected RoleDao roleDao;

        @Inject
        public OrganizationService(
                OrganizationDao organizationDao,
                UserDao userDao,
                RoleDao roleDao) {

            super(organizationDao, Organization.class);

            this.organizationDao = organizationDao;
            this.userDao = userDao;
            this.roleDao = roleDao;
        }

... additional methods
}

Мой класс локатора выглядит так:

public class CompanyServiceLocator implements ServiceLocator {

    protected Injector injector;

    public CompanyServiceLocator() {        
        injector = GuiceFactory.getInjector();
    }

    @Override
    public Object getInstance(Class<?> clazz) {
        return injector.getInstance(clazz);
    }
}

OrganizationProxy выглядит так:

@ProxyFor(value=Organization.class, locator=OrganizationService.class)
public interface OrganizationProxy extends CompanyEntityProxy {
... setters/getters defined here
}

Запрос организации выглядит так:

@Service(value=OrganizationService.class, locator=CompanyServiceLocator.class)
public interface OrganizationRequest extends RequestContext {
...
}

Код на стороне клиента выглядит примерно так:

OrganizationRequest req = organizationRequestFactory.request();
req.paginate(0, 10).fire(paginationReceiver); // Works!

req = organizationRequestFactory.request();
OrganizationProxy org = req.create(OrganizationProxy.class);
org.setName("test");
req.save(org).fire(receiver); // This causes the server side exception

Для меня очевидно, что ServiceLayerDecorator не может создавать экземпляры OrganizationService, потому что у него нет конструктора по умолчанию, но именно поэтому я использую Guice и перезаписал ServiceLocator, чтобы использовать Guice для создания экземпляров службы. Но почему первый вызов правильно использует мой ServiceLocator, а второй — нет?


person Brad    schedule 04.04.2011    source источник
comment
Какие аннотации @Proxy и @Service вы используете? Сообщение об ошибке выглядит так, как будто у вас есть экземпляр OrganizationService, отличный от экземпляра по умолчанию, который используется в качестве цели значения локатора в одной из аннотаций.   -  person BobV    schedule 04.04.2011
comment
Нет, я уверен, что они правы. Опять же, он отлично работает, когда я извлекаю данные. У меня проблема только при сохранении объекта. Я обновил вопрос, включив в него интерфейс прокси и контекст запроса, чтобы вы могли видеть.   -  person Brad    schedule 04.04.2011


Ответы (2)


Локаторы должны создавать экземпляры по умолчанию.

@ProxyFor(value=Organization.class, locator=OrganizationService.class)

это где вещи идут с рельсов. Если OrganizationService продает экземпляры Organization для выполнения интерфейса Locator, вам нужно сделать его экземпляром по умолчанию или внедрить ServiceLayerDecorator, который реализует createLocator().

Причина того, что первый пример кода работает, а второй нет, заключается в том, что второй образец кода создает и изменяет Organization на основе команд от клиента. В этом случае Locator.create() должен вызываться кодом сервера RequestFactory. Не зная, что paginate() возвращает клиенту, я подозреваю, что экземпляры Organization не возвращаются, поскольку было бы необходимо вызвать методы Locator.getId() и Locator.getVersion().

person BobV    schedule 05.04.2011
comment
Какова цель предоставления CompanyServiceLocator экземпляра OrganizationService? Службы (реализации Locator) теперь предоставляются/создаются двумя разными классами (ServiceLayerDecorator и реализациями ServiceLocator)? - person Brad; 05.04.2011
comment
Услуги предоставляются ServiceLocators; сущности предоставляются локаторами. RequestFactory создает любой из них (ServiceLocator или Locator), используя их конструктор без аргументов, если вы не предоставите ServiceLayerDecorator для изменения этого поведения. Возможно, вам будет трудно понять, кто что делает, потому что ваша служба и ваши объекты Locator of Operation относятся к одному и тому же классу в вашем случае (и, честно говоря, я не совсем понимаю, почему) - person Thomas Broyer; 07.04.2011
comment
@Thomas, у меня есть два разных класса: CompanyServiceLocator предоставляет сервис, а OrganizationService предоставляет объекты Locator of Operation. Я не понимаю, как это одно и то же? Возможно, я недостаточно хорошо подготовил вопрос, но я считаю, что обязанности разделены правильно. Если нет, я хотел бы услышать, почему, чтобы я мог их исправить. - person Brad; 07.04.2011
comment
Я имел в виду, что OrganizationService является одновременно вашим сервисом и Locator‹Operation›. Дело в том, что ваш CompanyServiceLocator будет предоставлять экземпляр в качестве службы (и если у вас есть такой ServiceLocator, это, вероятно, означает, что ваша служба не может быть просто создана с помощью конструктора по умолчанию), а RequestFactory будет использовать конструктор по умолчанию для создания экземпляра класса в качестве локатора. ‹Операция›. ИМО лучше иметь один класс для службы и другой в качестве локатора для сущностей. - person Thomas Broyer; 08.04.2011
comment
@ Томас, спасибо за разъяснение. Я вижу, что вы сейчас говорите. - person Brad; 08.04.2011

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

Расширьте RequestFactoryServlet по умолчанию, чтобы по умолчанию можно было внедрить свой собственный ServiceLayerDecorator.

public class CompanyRequestFactoryServlet extends RequestFactoryServlet {

    public CompanyRequestFactoryServlet() {
        this(new DefaultExceptionHandler(), new CompanyServiceLayerDecorator());
    }

    public SchedgyRequestFactoryServlet(ExceptionHandler exceptionHandler,
            ServiceLayerDecorator... serviceDecorators) {
        super(exceptionHandler, serviceDecorators);
    }
}

Создайте ServiceLayerDecorator для предоставления экземпляров вашего локатора. Я использую Guice, так что это было довольно просто. GuiceFactory в приведенном ниже коде — это просто синглтон, предоставляющий экземпляр Guice Injector.

public class CompanyServiceLayerDecorator extends ServiceLayerDecorator {

    @Override
    public <T extends Locator<?, ?>> T createLocator(Class<T> clazz) {
        return GuiceFactory.getInjector().getInstance(clazz);
    }
}

Наконец, обновите файл web.xml, чтобы использовать собственный сервлет:

<servlet>
    <servlet-name>requestFactoryServlet</servlet-name>
    <servlet-class>com.company.core.requestfactory.CompanyRequestFactoryServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>requestFactoryServlet</servlet-name>
    <url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>
person Brad    schedule 06.04.2011