Сбой отложенного связывания GWT RPC
Мы совместно разрабатываем веб-приложение GWT, в котором уже есть рабочие вызовы RPC для других модулей. Мы создали новый модуль RPC (на основе существующей архитектуры), который компилируется и запускается, но не генерирует исключение времени выполнения в следующей строке:
this.dashboardService = GWT.create(DashboardService.class);
Последняя строка в консоли - "Неопределенное исключение исключено", за которым следует трассировка стека до GWT.create()
строка выше, которой предшествует сообщение об ошибке консоли:
Отложенное связывание не удалось... ожидать последующих сбоев [ОШИБКА]
Перед этими двумя строками приведен подробный список красных ошибок, который начинается следующим образом:
[INFO] [(...)] - Модуль... был загружен
[DEBUG] [(...)] - Перепривязка (...). DashboardService
[DEBUG] [(...)] - вызов генератора com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator
[DEBUG] [(...)] - Создание клиентского прокси для интерфейса удаленного обслуживания '(...). DashboardService'
[INFO] [(...)] - проверка аргумента типа 0 типа 'java.util.Arrays.ArrayList', поскольку он представлен как массив с максимальным измерением 1 в этом типе или одном из его подтипов (достигается через (...).DashboardChannelSummary)
,,,(больше ошибок без трассировки стека или номеров строк)
Консоль спрашивает: "Вы забыли наследовать модуль?" но, основываясь на моих исследованиях, это не проблема; проблема где-то в процессе отложенного связывания GWT, который не виден в трассировке стека. Я подозреваю, что проблема в жирной строке выше, но я не могу описать это сообщение об ошибке без номера строки. Вот код с собственными именами пакетов / модулей, замененными на (...):
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;
и отложенная привязка, которая не работает в конструкторе:
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);
}
}
Мы дважды и трижды проверили наш код RPC на точность на основе документации и предложений по SO, таких как проверка правильности сигнатур методов во всех интерфейсах и реализациях. Что-то явно не так, выпрыгивая никому? Есть ли способ, которым мы можем отладить эту ошибку более подробно?
ОБНОВИТЬ
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;
}
}
1 ответ
Виноват скорее всего DashboardChannelSummary
, Чтобы быть уверенным, измените тип возврата getChannelSummary
что-то "безопасное", как String
или просто Void
, Если ошибка не устранена, существует проблема с конфигурацией службы (хотя, я сомневаюсь, что она возникнет на этапе компиляции GWT). Если этот сервис работает, то вы можете быть уверены, что это потому, что DashboardChannelSummary
не сериализуем.
В то время как сам класс имеет конструктор без аргументов и реализует IsSerializable
не все поля являются сериализуемыми. Вы должны более внимательно посмотреть на Channel
класс (возможно DocumentSummary
тоже, но в вопросе нет кода для этого) и Iterator
поля (ArrayList
возвращает Itr
экземпляр, который, кажется, не сериализуем).
Если ошибка не устранена, попробуйте упростить DashboardChannelSummary
пока вы не получите рабочую версию, а затем продолжите свой путь "вверх", пока не найдете ту часть, которая вызывает ошибку.