Search code examples
gwtpolymorphismrequestfactory

GWT polymorphic lists with @ExtraTypes


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!!


Solution

  • 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