1
votes

I have a little problem with a list that contains different types of elements and i would like to see if anyone of you have met the problem before. The issue should be solved with the use of @ExtraTypes, but it is not working for me, so i guess i am not using it correctly. So, the scenario is (bean names are changed for clarity):

GENERAL:

  • I am using GWT 2.5 with RequestFactory.

SERVER-SIDE:

  • I have a RootBean that contains, among other stuff, a
    List <ChildBean>
    .
  • This ChildBean contains some primitive attributes.
  • ChildBean is also extended by a MoreSpecificChildBean that inherits all the parent attributes and adds a few more.
  • The RootBean gets its list filled up with elements of type ChildBean and MoreSpecificChildBean depending on some logic.

CLIENT-SIDE:

  • IRootBeanProxy is a ValueProxy with these annotations:

     @ProxyFor (value = RootBean.class)
     @ExtraTypes ({IMoreSpecificChildBeanProxy.class})
    

and contains a list

List <IChildBeanProxy> getChildren ();
  • IChildBeanProxy is a ValueProxy:
    @ProxyFor (value=ChildBean)
    public interface IChildBeanProxy extends ValueProxy
    
  • IMoreSpecificChildBeanProxy is a ValueProxy:
    @ProxyFor (value=MoreSpecificChildBean)
    public interface IMoreSpecificChildBeanProxy extends IChildBeanProxy
    
  • the Request context has a method that returns Request and i added the @ExtraTypes annotation here too:
    @Service (value = CompareService.class, locator = SpringServiceLocator.class)
    @ExtraTypes ({IChildBeanProxy.class, IMoreSpecificChildBeanProxy.class})
    public interface ICompareRequestContext extends RequestContext {
       Request <IRootBeanProxy> compare (Integer id1, Integer id2);
    

Question

Supposedly with those annotations, RF should be aware of the existence of polymorphic inherited classes, but all i get in the client is an IRootBeanProxy with a list of IChildBeanProxy elements. This list includes the MoreSpecificChildBean, but in the shape of a IChildBeanProxy, so that i cannot tell it from the others. So i am wondering what i am doing wrong, if i am setting the ExtraTypes annotation at the wrong place or something.

Anyone?

Thx for all the help!!

1
Can you paste the portions of the generated DeobfuscatorBuilderImpl class related to your proxies and domain classes? Also try to set a breakpoint in com.google.web.bindery.requestfactory.server.ResolverServiceLayer#resolveClientType to see what happens there. - Thomas Broyer
Thx for the suggestion Thomas, i found out that the generated class had no reference to the inherited type at all, so of course resolveClient could not find the proxy class and fell back to its parent. I simply introduced a dummy method within my requestContext that actually returns the inherited type, and that forced its mapping to get into the generated file, and thus allowed ResolverServiceLayer to find it and map it correctly. It looks a bit weird, i still think i am doing something wrong with the ExtraFields declaration, but could it be some sort of bug anyway? Thx again!! - Gustavo Rodríguez
It might well be a bug. Try enabling verbose output by passing the verbose processor option (on the command line, that's be -Averbose) and see how it scans your interfaces (logs are emitted as warnings, that's so they show in Eclipse right in the editor) - Thomas Broyer
I get a bunch of [java] [Loaded xxx from file:yyyyy] logs. I diffed the output with and without dummy methods in my RequestContext, and they both seem identical (in regards to my classes), that is, they include all of them, even the inherited ones. The dummy method solution is ok for me now, but if you feel like it may be a bug and i can help in debugging, just let me know what else to check ;) - Gustavo Rodríguez
Could you please file a bug at code.google.com/p/google-web-toolkit/issues/entry ? Please include/attach a repro case (the proxies, request contexts and request factory, and the domain objects) to make it easier to reproduce and debug. - Thomas Broyer

1 Answers

1
votes

I do the exact same thing for quite a few classes but it will always return me the base type which I can iterate through and test for instanceof if needed. You will probably have to cast the object to the subclass. If you do not add the @ExtraTypes you will know because on the server side you will get a message stating that MoreSpecificChildBean cannot be sent to the client.

I only annotate the service and not the proxy, I ran into some quirks with 2.4 adding @ExtraTypes to the proxy.

/**
 * Base proxy that all other metric proxies extend. It is used mainly for it's
 * inheritence with the RequestFactory. It's concrete implementation is
 * {@link MetricNumber}.
 * 
 * @author chinshaw
 */
@ProxyFor(value = Metric.class, locator = IMetricEntityLocator.class)
public interface MetricProxy extends DatastoreObjectProxy {


    /**
     * Name of this object in the ui. This will commonly be extended by
     * subclasses.
     */
    public String NAME = "Generic Metric";

    /**
     * This is a list of types of outputs that the ui can support. This is
     * typically used for listing types of supported Metrics in the operation
     * output screen.
     * 
     * @author chinshaw
     */
    public enum MetricOutputType {
        MetricNumber, MetricString, MetricCollection, MetricStaticChart, MetricDynamicChart
    }

    /**
     * See {@link MetricNumber#setName(String)}
     * 
     * @param name
     */
    public void setName(String name);

    /**
     * See {@link MetricNumber#setContext(String)}
     * 
     * @return name of the metric.
     */
    public String getName();


    /**
     * Get the list of violations attached to this metric.
     * 
     * @return
     */
    public List<ViolationProxy> getViolations();
}

@ProxyFor(value = MetricNumber.class, locator = IMetricEntityLocator.class)
public interface MetricNumberProxy extends MetricProxy {

    public List<NumberRangeProxy> getRanges();

    public void setRanges(List<NumberRangeProxy> ranges);
}

...

@ProxyFor(value = MetricDouble.class, locator = IMetricEntityLocator.class)
public interface MetricDoubleProxy extends MetricNumberProxy {

    /* Properties when fetching the object for with clause */
    public static String[] PROPERTIES = {"ranges"};

    public Double getValue();
}

...

@ProxyFor(value = MetricPlot.class, locator = IMetricEntityLocator.class)
public interface MetricPlotProxy extends MetricProxy {

    /**
     * UI Name of the object.
     */
    public String NAME = "Static Plot";

    public String getPlotUrl();
}

This is a made up method from because I usually always return composite classes that may contain a list of metrics. That being said this will return me the base type of metrics, and then I can cast them.

@ExtraTypes({ MetricProxy.class, MetricNumberProxy.class, MetricDoubleProxy.class, MetricIntegerProxy.class})
@Service(value = AnalyticsOperationDao.class, locator = DaoServiceLocator.class)
public interface AnalyticsOperationRequest extends DaoRequest<AnalyticsOperationProxy> {

    Request<List<<MetricProxy>> getSomeMetrics();

}

Not an exact method I use but will work for getting a proxy of type.

context.getSomeMetrics().with(MetricNumber.PROPERTIES).fire(new Receiver<List<MetricProxy>>() {

  public void onSuccess(List<MetricProxy> metrics) {
      for (MetricProxy metric : metrics) {
          if (metric instanceof MetricDoubleProxy) {
              logger.info("Got a class of double " + metric.getValue());
          }
      }          
  }
}

You will know if you are missing an @ExtraTypes annotation when you get the error stated above.

Hope that helps