Search code examples
springgwtrequestfactory

requestfactory complain about find method


I have a spring (3.1) application with a service and dao layer. I try to use requestfactory (gwt 2.4) withi this spring layer.

Here some of my class

My domain class

public class Account {
  Long id;
  String username;
  // get, set
}

The bridge between spring and gwt

public class SpringServiceLocator implements ServiceLocator {

    @Override
    public Object getInstance(Class<?> clazz) {
        HttpServletRequest request = RequestFactoryServlet.getThreadLocalRequest();
        ServletContext servletContext = request.getSession().getServletContext();
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        return context.getBean(clazz);
    }
}

My account proxy

@ProxyFor(value=Account.class, locator = AccountLocator.class)
public interface AccountProxy extends EntityProxy{
    public Long getId();
    public String getUsername();

    public void setUsername(String userName);
    public void setId(Long id);    
}

RequestContext class

@Service(locator = SpringServiceLocator.class, value =AccountService.class)
public interface AccountRequest extends RequestContext {
    Request<List<AccountProxy>> loadAllAccounts();
}

My requestFactory class

public interface AccountRequestFactory extends RequestFactory {
    AccountRequest accountRequest();
}

My spring service

public interface AccountService {
    public List<Account> loadAllAccounts();
}

@Service
public class AccountServiceImpl implements AccountService{
    @Autowired
    private AccountDAO accountDAO;
}

Account locator to avoid to put method in the entity

public class AccountLocator extends Locator<Account, Long> {

    @Autowired
    private AccountDAO accountDAO;

    @Override
    public Account create(Class<? extends Account> clazz) {
       return new Account();

    }
}

applicationContext.xml

<context:annotation-config />
<context:component-scan base-package="com.calibra" />
<bean id="accountService" class="org.calibra.server.service.AccountServiceImpl"/>
<bean id="accountDAO" class="org.calibra.server.dao.AccountDAOImpl"/>

The demo work but i get this error:

com.google.web.bindery.requestfactory.server.UnexpectedException: Could not find static method with a single parameter of a key type

Also on my AccountProxy i get this complain (a warning)

The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method. Attempting to send a AccountProxy to the server will result in a server error.

I don't want to add a find methond in my domain class. I tried to put this method in my spring service, but i get the same warning.

Edit with the Locator that work fine

Just strange i need to put bean in the applicationContext, context:annotation and context:component-scan seem useless

Any idea?


Solution

  • The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method.

    If you don't provide a find method of some kind, RequestFactory has no way of reconstituting objects when they get to the server - it can only create brand new ones, which prevents it from merging with existing data. Take this away, and you might as well have RPC again.

    If you don't want static methods, provide a Locator instance which is able to find objects. From https://developers.google.com/web-toolkit/doc/latest/DevGuideRequestFactory#locators:

    What if you don't want to implement persistence code in an entity itself? To implement the required entity locator methods, create an entity locator class that extends Locator:

    public class EmployeeLocator extends Locator<Employee, Long> {
      @Override
      public Employee create(Class<? extends Employee> clazz)
      {
        return new Employee();
      }
      ...
    }
    

    Then associate it with the entity in the @ProxyFor annotation:

    @ProxyFor(value = Employee.class, locator = EmployeeLocator.class)
    public interface EmployeeProxy extends EntityProxy {
      ...
    }
    

    You'll need to implement all of the methods, not just create - and the main one you are interested in is find(Class, Long). It may be possible to use one single Locator type for all proxies - as of 2.4.0 and 2.5.0-rc1 it is safe to fail to implement getDomainType(), and all of the other methods that need to know the exact type are provided with it as an argument.

    Here is an example of what this can look like with JPA and Guice, but I think the idea is clear enough that it can be implemented with Spring and whatever persistence mechanism you are using. Here, all entities are expected to implement HasVersionAndId, allowing the locator to generalize on how to invoke getVersion and getId - you might have your own base class for all persisted entities.

    (from https://github.com/niloc132/tvguide-sample-parent/blob/master/tvguide-client/src/main/java/com/acme/gwt/server/InjectingLocator.java)

    public class InjectingLocator<T extends HasVersionAndId> extends Locator<T, Long> {
      @Inject
      Provider<EntityManager> data;
    
      @Inject
      Injector injector;
    
      @Override
      public T create(Class<? extends T> clazz) {
        return injector.getInstance(clazz);
      }
    
      @Override
      public T find(Class<? extends T> clazz, Long id) {
        return data.get().find(clazz, id);
      }
    
      @Override
      public Class<T> getDomainType() {
        throw new UnsupportedOperationException();//unused
      }
    
      @Override
      public Long getId(T domainObject) {
        return domainObject.getId();
      }
    
      @Override
      public Class<Long> getIdType() {
        return Long.class;
      }
    
      @Override
      public Object getVersion(T domainObject) {
        return domainObject.getVersion();
      }
    }