title: GWT RPC Deferred Binding Failed
body: <p>We are co-developing a GWT web app that already has working RPC calls for other modules. We built a new RPC module (based on the existing architecture) that compiles and runs, but fails throwing a run-time exception on the following line:</p>
  
  <pre><code>this.dashboardService = GWT.create(DashboardService.class);
  </code></pre>
  
  <p>The last line in the console is "Uncaught exception escaped" followed by the stack trace to the <code>GWT.create()</code> line above, which is preceded by the console error message:</p>
  
  <blockquote>
    <p>Deferred binding failed for ... expect subsequent failures [ERROR]</p>
  </blockquote>
  
  <p>Before those two lines is a laundry list of red errors that begins as follows:</p>
  
  <p>[INFO] [(...)] - Module ... has been loaded</p>
  
  <p>[DEBUG] [(...)] - Rebinding (...).DashboardService</p>
  
  <p>[DEBUG] [(...)] - Invoking generator com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator</p>
  
  <p>[DEBUG] [(...)] - Generating client proxy for remote service interface '(...).DashboardService'</p>
  
  <p><strong>[INFO] [(...)] - Checking type argument 0 of type 'java.util.Arrays.ArrayList' because it is exposed as an array with a maximum dimension of 1 in this type or one of its subtypes (reached via (...).DashboardChannelSummary)</strong></p>
  
  <p>.
  .
  .
  <em>(more errors without stack trace or line numbers)</em></p>
  
  <p>The console asks "Did you forget to inherit a module?" but based upon my research, this is not the problem; the problem is somewhere in the GWT deferred binding process, which is not visible in the stack trace. I suspect the bolded line above to be the issue but I cannot make heads or tales of this error message without a line number. Here is the code with proprietary package /module names replaced with (...):</p>
  
  <p><strong>web.xml</strong></p>
  
  <pre><code>&lt;servlet&gt;
      &lt;servlet-name&gt;(...) DashboardService
      &lt;/servlet-name&gt;
      &lt;servlet-class&gt;(...).server.DashboardServiceImpl
      &lt;/servlet-class&gt;
  &lt;/servlet&gt;
  
  &lt;servlet-mapping&gt;
      &lt;servlet-name&gt;(...) DashboardService
      &lt;/servlet-name&gt;
      &lt;url-pattern&gt;/(...)/DashboardService&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;
  </code></pre>
  
  <p><strong>DashboardChannelSummary.java</strong></p>
  
  <pre><code>/**
   * This class is an in memory representation of the results of a document search
   */
  public class DashboardChannelSummary implements IsSerializable {
      /** Only searches for documents from the past in this amount (the past week) */
      private static final int DEFAULT_DASHBOARD_HISTORY_IN_DAYS = -7;
      /** array of channels */
      private List&lt;Channel&gt; channels;
      /** iterator */
      private Iterator&lt;Channel&gt; iterator;
      /** */
      private final static String IMAGE_PATH = "/images/channels/";
      /** */
      private final static String IMAGE_EXT = ".png";
      /** constant for the channel header name  */
      public final static String BUSINESS_LABEL = "business aviation";
      /** constant for the channel header name  */
      public final static String COMMERCIAL_LABEL = "commercial aviation";
      /** constant for the channel header name  */
      public final static String MRO_LABEL = "mro";
      /** constant for the channel header name  */
      public final static String DEFENSE_LABEL = "defense";
      /** 
       * 
       */
      public enum CHANNEL_NAME {
          BUSINESS   (BUSINESS_LABEL,   DocumentSummary.BA_ID), 
          COMMERCIAL (COMMERCIAL_LABEL, DocumentSummary.CA_ID), 
          MRO        (MRO_LABEL,        DocumentSummary.MRO_ID),
          DEFENSE    (DEFENSE_LABEL,    DocumentSummary.DEFENSE_ID);
          /** */
          public String label;
          /** */
          public int ID;
          /** */
          private CHANNEL_NAME(String label, int ID) {
              this.label = label.toUpperCase();
              this.ID = ID;
          }
      };
  
      /**
       * 
       */
      public static List&lt;String&gt; channelNames() {
          ArrayList&lt;String&gt; channels = new ArrayList&lt;String&gt;(CHANNEL_NAME.values().length);
          for(int i=0; i&lt;channels.size(); i++) {
              channels.add(CHANNEL_NAME.values()[i].label);
          }
          return channels;
      }
  
      /**
       * 
       */
      public static int[] channelIDs() {
          int[] IDs = new int[CHANNEL_NAME.values().length];
          for(int i=0; i&lt;IDs.length; i++) {
              IDs[i] = CHANNEL_NAME.values()[i].ID;
          }
          return IDs;
      }
  
      /**
       * 
       * @return
       */
      public static int channelCount() {
          return CHANNEL_NAME.values().length;
      }
  
      /**
       * 
       */
      public static Date cutoffDate() {
          Date date = new Date(0);
          CalendarUtil.addDaysToDate(date, DEFAULT_DASHBOARD_HISTORY_IN_DAYS);
          return date;
      }
  
      /**
       * 
       */
      public class Channel {
          /** the name of this channel */
          private CHANNEL_NAME name;
          /** The list of documents */
          private List&lt;DocumentSummary&gt; docs;
          /** the iterator */
          private Iterator&lt;DocumentSummary&gt; iterator; 
  
          /**
           * 
           */
          public Channel(List&lt;DocumentSummary&gt; docs, CHANNEL_NAME name) {
              this.docs = docs;
              this.name = name;
              iterator = docs.iterator();
          }
  
          /**
           * 
           */
          public String getLabel() {
              return name.label;
          }
  
          /**
           * 
           */
          public List&lt;DocumentSummary&gt; getDocuments() {
              return docs;
          }
  
          /**
           * 
           */
          public boolean hasDocuments() {
              return iterator.hasNext();
          }
  
          /**
           * 
           * @return
           */
          public DocumentSummary nextDocument() {
              if(iterator.hasNext()) {
                  return iterator.next();
              }
              else {
                  return null;
              }
          }
  
          /**
           * 
           */
          public String nextImageURL() {
              return GWT.getHostPageBaseURL().concat(IMAGE_PATH + String.valueOf(Random.nextInt(channels.size()) - 1) + IMAGE_EXT);
          }
      }
  
      /**
       * Constructor
       */
      public DashboardChannelSummary() {
          channels = new ArrayList&lt;Channel&gt;(CHANNEL_NAME.values().length);
          iterator = channels.iterator();
      }
  
      /**
       * Constructor
       */
      public DashboardChannelSummary(List&lt;List&lt;DocumentSummary&gt;&gt; channelList) {
          channels = new ArrayList&lt;Channel&gt;(CHANNEL_NAME.values().length);
          iterator = channels.iterator();
          int count = 0;
          for(List&lt;DocumentSummary&gt; channelData : channelList)
          {
              channels.add(new Channel(channelData, CHANNEL_NAME.values()[count++]));
          }
      }
  
      /**
       * @return 
       */
      public List&lt;Channel&gt; getChannels() {
          return channels;
      }
  
      /**
       * @return 
       */
      public Channel getChannel(int channel) {
          return channels.get(channel);
      }
  
      /**
       * @return 
       */
      public Channel nextChannel() {
          if(iterator.hasNext()) {
              return iterator.next();
          }
          else {
              return null;
          }
      }
  
      /**
       * @return 
       */
      public List&lt;DocumentSummary&gt; getDocuments(int channel) {
          return this.getChannel(channel).getDocuments();
      }
  }
  </code></pre>
  
  <p><strong>DashboardPresenter.java:</strong></p>
  
  <pre><code>private final DashboardServiceAsync dashboardService;
  </code></pre>
  
  <p><em>and the deferred binding that fails in the Constructor:</em></p>
  
  <p><code>this.dashboardService = GWT.create(DashboardService.class);</code></p>
  
  <p><strong>DashboardServiceAsync.java:</strong></p>
  
  <pre><code>public interface DashboardServiceAsync {
  
      /**
       * 
       * @param channelIDs
       * @param startDate
       * @param async
       */
      void getChannelSummary(int[] channelIDs, Date startDate, AsyncCallback&lt;DashboardChannelSummary&gt; async);
  }
  </code></pre>
  
  <p><strong>DashboardService.java:</strong></p>
  
  <pre><code>@RemoteServiceRelativePath("DashboardService") public interface DashboardService extends RemoteService {
  
      /**
       * 
       * @param channelIDs
       * @param startDate
       * @return
       */
      DashboardChannelSummary getChannelSummary(int[] channelIDs, Date startDate); 
  }
  </code></pre>
  
  <p>On the server:</p>
  
  <p><strong>DashboardServiceImpl.java:</strong></p>
  
  <pre><code>public class DashboardServiceImpl extends RemoteServiceServlet implements DashboardService {
  
      /**
       * 
       * @param channelIDs
       * @param startDate
       * @return
       */
      @Override
      public DashboardChannelSummary getChannelSummary(int[] channelIDs, Date startDate) {
          return new DashboardDaoImpl().getChannelSummary(channelIDs, startDate);
      }
  }
  </code></pre>
  
  <p>We have double and triple checked our RPC code for accuracy based on the <a href="http://www.gwtproject.org/doc/latest/DevGuideServerCommunication.html#DevGuideRemoteProcedureCalls" rel="nofollow">documentation</a> and the suggestions on SO, such as making sure the method signatures are correct across all interfaces and implementations. Anything obviously wrong jumping out to anyone? Is there a way we can we debug this error with more detail?</p>
  
  <p><strong>UPDATE</strong></p>
  
  <p><em>DashboardChannelSummary.java</em> redesigned for max efficiency in transporting the data from the server to the client, with all properties now "serializable:"</p>
  
  <pre><code>/**
   * This class is an in memory representation of the results of a document search.
   */
  public class DashboardChannelSummary implements IsSerializable {
      /** Only searches for documents from the past in this amount (the past week) */
      private static final int DEFAULT_DASHBOARD_HISTORY_IN_DAYS = -7;
      /** array of channels */
      private ArrayList&lt;ArrayList&lt;DocumentSummary&gt;&gt; channels;
      /** */
      private int channel = 0;
      /** */
      private int image = 0;
      /** */
      private int index = 0;
      /** */
      private int last = 0;
      /** */
      private final static String IMAGE_PATH = "images/channels/";
      /** */
      private final static String IMAGE_EXT = ".jpg";
      /** constant for the channel header name  */
      public final static String BUSINESS_LABEL = "business";
      /** constant for the channel header name  */
      public final static String COMMERCIAL_LABEL = "commercial";
      /** constant for the channel header name  */
      public final static String MRO_LABEL = "mro";
      /** constant for the channel header name  */
      public final static String DEFENSE_LABEL = "defense";
      /** 
       * 
       */
      public enum CHANNEL_NAME {
          BUSINESS   (BUSINESS_LABEL,   DocumentSummary.BA_ID,      "bus"), 
          COMMERCIAL (COMMERCIAL_LABEL, DocumentSummary.CA_ID,     "_com"), 
          MRO        (MRO_LABEL,        DocumentSummary.MRO_ID,     "mro"),
          DEFENSE    (DEFENSE_LABEL,    DocumentSummary.DEFENSE_ID, "mil");
          /** */
          public String label;
          /** */
          public int ID;
          /** */
          public String prefix;
          /** */
          private CHANNEL_NAME(String label, int ID, String prefix) {
              this.label = label.toUpperCase();
              this.ID = ID;
              this.prefix = prefix;
          }
      };
  
      /**
       * 
       */
      private String nextRandomImage() {
          while(index == last) {
              index = Random.nextInt(channels.size()) + 1;
          }
          last = index;
          return String.valueOf(index);
      }
  
      /**
       * 
       */
      public static List&lt;String&gt; channelNames() {
          ArrayList&lt;String&gt; channels = new ArrayList&lt;String&gt;(CHANNEL_NAME.values().length);
          for(int i=0; i&lt;channels.size(); i++) {
              channels.add(CHANNEL_NAME.values()[i].label);
          }
          return channels;
      }
  
      /**
       * 
       */
      public static int[] channelIDs() {
          int[] IDs = new int[CHANNEL_NAME.values().length];
          for(int i=0; i&lt;IDs.length; i++) {
              IDs[i] = CHANNEL_NAME.values()[i].ID;
          }
          return IDs;
      }
  
      /**
       * 
       * @return
       */
      public static int channelCount() {
          return CHANNEL_NAME.values().length;
      }
  
      /**
       * 
       */
      public static Date cutoffDate() {
          Date date = new Date();
          CalendarUtil.addDaysToDate(date, DEFAULT_DASHBOARD_HISTORY_IN_DAYS);
          return date;
      }
  
  
      /**
       * Constructor
       */
      public DashboardChannelSummary() {
      }
  
      /**
       * Constructor
       */
      public DashboardChannelSummary(ArrayList&lt;ArrayList&lt;DocumentSummary&gt;&gt; channels) {
          this.channels = channels;
      }
  
      /**
       * 
       */
      public String nextImageURL() {
          if(++image &gt; channels.get(channel - 1).size()) {
              image = 0;
          }
          return GWT.getHostPageBaseURL() +
                 IMAGE_PATH + 
                 CHANNEL_NAME.values()[channel - 1].prefix + 
                 nextRandomImage() + 
                 IMAGE_EXT;
      }
  
      /**
       * 
       */
      public ArrayList&lt;DocumentSummary&gt; nextChannel() {
          return channels.get(channel++);
      }
  
      /**
       * 
       */
      public String toString() {
          return this.getClass().toString() + " current channel : " + channel;
      }
  }
  </code></pre>