【问题标题】:GWT client not receiving IncompatibleRemoteServiceException when serialization policy changes序列化策略更改时,GWT 客户端未收到 IncompatibleRemoteServiceException
【发布时间】:2012-02-10 18:40:57
【问题描述】:

当部署新版本的应用程序并更改模型时 类(例如添加/删除字段)。 运行旧版本的客户端获取 com.google.gwt.user.client.rpc.SerializationException 与 RPC 使用旧客户端代码制作,这是预期的行为。 因此,我们希望在客户端看到 IncompatibleRemoteServiceException。 但是我们得到了 StatusCodeException。

StatusCodeException 是 500 错误,我们无法自定义客户端 很容易出现副作用(我们不想假设每个 StatusCodeException 或 500 错误是新版本)。我们能做什么 在这里做错了吗?

注意:在服务器端(日志),我们显然会得到 SerializationExcepion,因为旧客户端的序列化策略对新服务器不再有效。那么为什么不抛出 IncompatibleRemoteServiceException 呢?

谢谢。

【问题讨论】:

    标签: java google-app-engine gwt exception-handling


    【解决方案1】:

    这是我面对这个问题的解决方案:

    首先扩展RemoteServiceServlet(您的所有服务servlet 都将从这个新类扩展,记住这是服务器代码)。这是那里的相关代码:

    @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 类的原因之一;当然用你自己的风格来警告用户。

    编辑:这是怎么回事?

    在 GWT 1.3.3 之前,IsSerializable 是唯一可用于将类标记为 GWT RPC 可序列化的接口。下一个版本出于相同目的接受了 Java 标准 Serializable 接口,但增加了对实现此接口的对象的安全策略的要求。默认情况下,GWT 为每个编译生成一个具有唯一哈希名称的策略。尝试传输标记为Serializable 的对象的旧客户端将在服务器端引发序列化策略异常,该异常将在客户端作为通用运行时错误接收。 IsSerializable 允许旧客户端仍然使用新服务,只要签名保持不变。这意味着这个问题的替代解决方案是将通过 GWT RPC 的每个对象标记为IsSerializable。但是,如果出于某种原因您需要不将对象引用到 GWT 接口,这对于旧客户端连接来说是一个不错的解决方案。

    查看GWT's guide 了解有关 1.3.3 回退的更多详细信息。

    【讨论】:

    • 虽然这在 GWT 的较新版本(即 2.5.1)中可能不是问题,但请注意,您不能仅通过从您的系统中抛出 IncompatibleRemoteServiceException 来测试功能。 service 方法,因为 GWT 会将其包装在 StatusCodeException 中。但是,如果您通过实际运行与您的客户端不兼容的服务来测试它,您将看到 IncompatibleRemoteServiceException 出现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-30
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多