PAINKILLER
PAINKILLER

Reputation: 73

GWT RPC Deferred Binding Failed

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:

this.dashboardService = GWT.create(DashboardService.class);

The last line in the console is "Uncaught exception escaped" followed by the stack trace to the GWT.create() line above, which is preceded by the console error message:

Deferred binding failed for ... expect subsequent failures [ERROR]

Before those two lines is a laundry list of red errors that begins as follows:

[INFO] [(...)] - Module ... has been loaded

[DEBUG] [(...)] - Rebinding (...).DashboardService

[DEBUG] [(...)] - Invoking generator com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator

[DEBUG] [(...)] - Generating client proxy for remote service interface '(...).DashboardService'

[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)

. . . (more errors without stack trace or line numbers)

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 (...):

web.xml

<servlet>
    <servlet-name>(...) DashboardService
    </servlet-name>
    <servlet-class>(...).server.DashboardServiceImpl
    </servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>(...) DashboardService
    </servlet-name>
    <url-pattern>/(...)/DashboardService</url-pattern>
</servlet-mapping>

DashboardChannelSummary.java

/**
 * 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<Channel> channels;
    /** iterator */
    private Iterator<Channel> 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<String> channelNames() {
        ArrayList<String> channels = new ArrayList<String>(CHANNEL_NAME.values().length);
        for(int i=0; i<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<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<DocumentSummary> docs;
        /** the iterator */
        private Iterator<DocumentSummary> iterator; 

        /**
         * 
         */
        public Channel(List<DocumentSummary> docs, CHANNEL_NAME name) {
            this.docs = docs;
            this.name = name;
            iterator = docs.iterator();
        }

        /**
         * 
         */
        public String getLabel() {
            return name.label;
        }

        /**
         * 
         */
        public List<DocumentSummary> 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<Channel>(CHANNEL_NAME.values().length);
        iterator = channels.iterator();
    }

    /**
     * Constructor
     */
    public DashboardChannelSummary(List<List<DocumentSummary>> channelList) {
        channels = new ArrayList<Channel>(CHANNEL_NAME.values().length);
        iterator = channels.iterator();
        int count = 0;
        for(List<DocumentSummary> channelData : channelList)
        {
            channels.add(new Channel(channelData, CHANNEL_NAME.values()[count++]));
        }
    }

    /**
     * @return 
     */
    public List<Channel> 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<DocumentSummary> getDocuments(int channel) {
        return this.getChannel(channel).getDocuments();
    }
}

DashboardPresenter.java:

private final DashboardServiceAsync dashboardService;

and the deferred binding that fails in the Constructor:

this.dashboardService = GWT.create(DashboardService.class);

DashboardServiceAsync.java:

public interface DashboardServiceAsync {

    /**
     * 
     * @param channelIDs
     * @param startDate
     * @param async
     */
    void getChannelSummary(int[] channelIDs, Date startDate, AsyncCallback<DashboardChannelSummary> async);
}

DashboardService.java:

@RemoteServiceRelativePath("DashboardService") public interface DashboardService extends RemoteService {

    /**
     * 
     * @param channelIDs
     * @param startDate
     * @return
     */
    DashboardChannelSummary getChannelSummary(int[] channelIDs, Date startDate); 
}

On the server:

DashboardServiceImpl.java:

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);
    }
}

We have double and triple checked our RPC code for accuracy based on the documentation 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?

UPDATE

DashboardChannelSummary.java redesigned for max efficiency in transporting the data from the server to the client, with all properties now "serializable:"

/**
 * 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<ArrayList<DocumentSummary>> 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<String> channelNames() {
        ArrayList<String> channels = new ArrayList<String>(CHANNEL_NAME.values().length);
        for(int i=0; i<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<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<ArrayList<DocumentSummary>> channels) {
        this.channels = channels;
    }

    /**
     * 
     */
    public String nextImageURL() {
        if(++image > channels.get(channel - 1).size()) {
            image = 0;
        }
        return GWT.getHostPageBaseURL() +
               IMAGE_PATH + 
               CHANNEL_NAME.values()[channel - 1].prefix + 
               nextRandomImage() + 
               IMAGE_EXT;
    }

    /**
     * 
     */
    public ArrayList<DocumentSummary> nextChannel() {
        return channels.get(channel++);
    }

    /**
     * 
     */
    public String toString() {
        return this.getClass().toString() + " current channel : " + channel;
    }
}

Upvotes: 1

Views: 1179

Answers (1)

Igor Klimer
Igor Klimer

Reputation: 15321

The culprit is most likely DashboardChannelSummary. To be sure, change the return type of getChannelSummary to something "safe", like String or just Void. If the error persists, there's an issue with the service's configuration (though, I doubt it would come up during GWT's compilation phase). If this service works, then you can be pretty sure that it's because DashboardChannelSummary is not serializable.

While the class itself has a no-args constructor and implements IsSerializable, not all the fields are serializable. You should have a closer look at the Channel class (maybe DocumentSummary too, but there's no code for it provided in the question) and the Iterator fields (ArrayList returns an Itr instance, which doesn't seem to be serializable).

If the error still persists, try simplifying DashboardChannelSummary until you get a working version and then work your way "up", until you find the part that is causing the error.

Upvotes: 1

Related Questions