Вот мое решение этой проблемы:
Первое расширение RemoteServiceServlet
(все ваши сервисные сервлеты будут расширяться из этого нового класса, помните, что это код сервера).Вот соответствующий код:
@Override
protected SerializationPolicy doGetSerializationPolicy(
HttpServletRequest request, String moduleBaseURL, String strongName) {
SerializationPolicy sp = super.doGetSerializationPolicy(request,
moduleBaseURL, strongName);
if(sp == null) { //no policy found, probably wrong client version
throw new InvalidPolicyException();
}
return sp;
}
и
@Override
protected void doUnexpectedFailure(Throwable e) {
if(e instanceof InvalidPolicyException) {
sendError(); //send message to reload client (wrong client version)
return; //that's it
}
super.doUnexpectedFailure(e);
}
Закрытый метод sendError()
- это, в основном, настраиваемая копия кода ошибки GWT 500 (извините за уродливый отступ Google))
private void sendError() {
ServletContext servletContext = getServletContext();
HttpServletResponse response = getThreadLocalResponse();
try {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_CONFLICT);
try {
response.getOutputStream().write("wrong client version".getBytes("UTF-8"));
} catch (IllegalStateException e) {
// Handle the (unexpected) case where getWriter() was previously used
response.getWriter().write("wrong client version");
}
} catch (IOException ex) {
servletContext.log(
"sendError failed while sending the previous custom failure to the client", ex);
}
}
Я использовал HttpServletResponse.SC_CONFLICT
(409), но вы, вероятно, можете использовать умный код ошибки.Написанное сообщение не очень важно.
Тогда в вашем пользовательском RpcRequestBuilder
(это код клиента)
public class CustomRequestBuilder extends RpcRequestBuilder {
private class RequestCallbackWrapper implements RequestCallback {
private RequestCallback callback;
RequestCallbackWrapper(RequestCallback aCallback) {
this.callback = aCallback;
}
@Override
public void onResponseReceived(Request request, Response response) {
if(response.getStatusCode() == 409) { //wrong client version
Navigator.closeEveryPopUp();
Navigator.showUncloseablePopUp("Login again!",
new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
//reload your $#%^ client!!!
Window.Location.reload();
}
});
} else {
//(...)irrelevant security code here(...)
callback.onResponseReceived(request, response);
}
}
@Override
public void onError(Request request, Throwable exception) {
callback.onError(request, exception);
}
}
@Override
protected void doFinish(RequestBuilder rb) {
//(...)more irrelevant security code here(...)
super.doFinish(rb);
rb.setCallback(new RequestCallbackWrapper(rb.getCallback()));
}
}
Мы используем несколько всплывающих окон, это одна из причин для класса Navigator;конечно, используйте свой собственный стиль, чтобы предупредить пользователя.
EDIT : Что здесь происходит?
До GWT 1.3.3 IsSerializable
был единственным доступным интерфейсомпометить класс как сериализуемый GWT RPC.Следующая версия принимала стандартный интерфейс Java Serializable
для той же цели, но добавляла требование политики безопасности для объектов, реализующих этот интерфейс.По умолчанию GWT генерирует политику для каждой компиляции с уникальным хэш-именем.Старый клиент, пытающийся передать объект, помеченный как Serializable
, на стороне сервера выдаст исключение политики сериализации, которое будет получено на стороне клиента как общая ошибка времени выполнения.IsSerializable
позволяет старым клиентам по-прежнему использовать новые службы, пока подпись остается неизменной.Это означает, что альтернативное решение для этого вопроса - пометить каждый объект, проходящий через GWT RPC, как IsSerializable
.Но если по какой-то причине вам нужно, чтобы ваши объекты не ссылались на интерфейс GWT, это хорошее решение для подключений старых клиентов.
Проверьте Руководство GWT для получения более подробной информации о 1.3.3 запасных.