Reputation: 5488
I am using GWT 2.2 with RequestFactory. The app has an existing service layer (server side) so I am using a ServiceLocator to provide these implementations. My Proxy and RequestContexts specify the correct service and locator to use (as shown here). I am able to make basic requests for data but when I try to save, I get the following exception:
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 is defined like:
// 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
}
My locator class looks like:
public class CompanyServiceLocator implements ServiceLocator {
protected Injector injector;
public CompanyServiceLocator() {
injector = GuiceFactory.getInjector();
}
@Override
public Object getInstance(Class<?> clazz) {
return injector.getInstance(clazz);
}
}
The OrganizationProxy looks like:
@ProxyFor(value=Organization.class, locator=OrganizationService.class)
public interface OrganizationProxy extends CompanyEntityProxy {
... setters/getters defined here
}
The OrganizationRequest looks like:
@Service(value=OrganizationService.class, locator=CompanyServiceLocator.class)
public interface OrganizationRequest extends RequestContext {
...
}
The client side code looks something like:
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
It is obvious to me that ServiceLayerDecorator cannot instantiate OrganizationService because it does not have a default constructor, but this is why I am using Guice and have over-written the ServiceLocator to use Guice to create instances of the service. But why does the first call correctly use my ServiceLocator whereas the second does not?
Upvotes: 1
Views: 2082
Reputation: 5488
I am still confused on why two classes need to provide implementations for Locators, but here is how I was able to fix this problem.
Extend the default RequestFactoryServlet so that you can inject your custom ServiceLayerDecorator by default.
public class CompanyRequestFactoryServlet extends RequestFactoryServlet {
public CompanyRequestFactoryServlet() {
this(new DefaultExceptionHandler(), new CompanyServiceLayerDecorator());
}
public SchedgyRequestFactoryServlet(ExceptionHandler exceptionHandler,
ServiceLayerDecorator... serviceDecorators) {
super(exceptionHandler, serviceDecorators);
}
}
Create a ServiceLayerDecorator to provide instances of your Locator's. I use Guice, so this was quite easy. GuiceFactory in the below code is just a singleton that provides an instance of the Guice Injector.
public class CompanyServiceLayerDecorator extends ServiceLayerDecorator {
@Override
public <T extends Locator<?, ?>> T createLocator(Class<T> clazz) {
return GuiceFactory.getInjector().getInstance(clazz);
}
}
Finally, update your web.xml to use your custom servlet:
<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>
Upvotes: 1
Reputation: 4173
Locators must be default-instantiable.
@ProxyFor(value=Organization.class, locator=OrganizationService.class)
is where things are going off the rails. If the OrganizationService
is vending instances of Organization
to fulfill the Locator
interface, you'll need to make it default-instantiable or inject a ServiceLayerDecorator
that implements createLocator()
.
The reason that the first code sample works and not the second is that the second code sample is creating and mutating an Organization
based on commands from the client. In this case Locator.create()
must be called by the RequestFactory server code. Without knowing what paginate()
returns to the client, I suspect that no instances of Organization
are being returned since it would be necessary to call the Locator.getId()
and Locator.getVersion()
methods.
Upvotes: 4