我们正在共同开发一个 GWT Web 应用程序,该应用程序已经可以对其他模块进行有效的 RPC 调用。我们构建了一个新的 RPC 模块(基于现有架构),该模块可以编译并运行,但无法在以下行抛出运行时异常:
this.dashboardService = GWT.create(DashboardService.class);
控制台中的最后一行是“未捕获的异常转义”,后面是上面的 GWT.create()
行的堆栈跟踪,前面是控制台错误消息:
Deferred binding failed for ... expect subsequent failures [ERROR]
这两行之前是一系列红色错误,开头如下:
[信息] [(...)] - 模块...已加载
[DEBUG] [(...)] - 重新绑定(bind) (...).DashboardService
[DEBUG] [(...)] - 调用生成器 com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator
[DEBUG] [(...)] - 为远程服务接口(interface)“(...).DashboardService”生成客户端代理
[INFO] [(...)] - 检查“java.util.Arrays.ArrayList”类型的类型参数 0,因为它在此类型或其子类型之一中公开为最大维度为 1 的数组(通过 (...).DashboardChannelSummary 访问)
。 。 。 (没有堆栈跟踪或行号的更多错误)
控制台询问“您是否忘记继承模块?”但根据我的研究,这不是问题所在;问题出在 GWT 延迟绑定(bind)过程中的某个地方,该过程在堆栈跟踪中不可见。我怀疑上面的粗体行是问题所在,但如果没有行号,我无法对这个错误消息进行头脑或故事。以下是专有包/模块名称替换为 (...) 的代码:
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;
以及构造函数中失败的延迟绑定(bind):
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);
}
在服务器上:
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);
}
}
我们根据 documentation 仔细检查了 RPC 代码的准确性。以及对 SO 的建议,例如确保所有接口(interface)和实现中的方法签名都是正确的。有什么明显的错误向任何人跳出来吗?有没有办法可以更详细地调试此错误?
更新
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 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;
}
}
最佳答案
罪魁祸首很可能是DashboardChannelSummary
。可以肯定的是,请将 getChannelSummary
的返回类型更改为“安全”的类型,例如 String
或只是 Void
。如果错误仍然存在,则服务的配置存在问题(不过,我怀疑它会在 GWT 编译阶段出现)。如果此服务有效,那么您可以非常确定这是因为 DashboardChannelSummary
不可序列化。
虽然类本身有一个无参数构造函数并实现 IsSerialized
,但并非所有字段都是可序列化的。您应该仔细查看 Channel
类(也可能是 DocumentSummary
,但问题中没有提供它的代码)和 Iterator
字段(ArrayList
返回一个 Itr
实例,该实例似乎不可序列化)。
如果错误仍然存在,请尝试简化DashboardChannelSummary
,直到获得工作版本,然后“向上”工作,直到找到导致错误的部分。
关于java - GWT RPC 延迟绑定(bind)失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26496348/