如何添加一个types到GWT的序列化策略白名单?
GWT的序列化程序对java.io.Serializable
支持有限,但出于安全原因,它有一个支持types的白名单。 我find的文档,例如这个FAQ条目说,你想要序列化的任何types“必须包含在序列化策略白名单中”,并且列表是在编译时生成的,但是不能解释编译器如何决定白名单上发生了什么。
生成的列表包含许多属于标准库的types,如java.lang.String
和java.util.HashMap
。 尝试序列化实现Serializable
接口但不在白名单上的java.sql.Date
时出现错误。 我怎样才能将这种types添加到列表中?
包含在服务接口中的任何特定types及其引用的任何types都将自动列入白名单,只要它们实现java.io.Serializable,例如:
public String getStringForDates(ArrayList<java.util.Date> dates);
将导致ArrayList和Date都包含在白名单中。
如果您尝试使用java.lang.Object而不是特定types,则会变得更复杂:
public Object getObjectForString(String str);
因为编译器不知道白名单。 在这种情况下,如果服务接口中的任何地方都没有引用这些对象,则必须使用IsSerializable接口将它们显式标记,否则它们不会让您通过RPC机制传递它们。
有一个解决方法:定义一个新的Dummy
类,其中包含所有想要包含在序列化中的types的成员字段。 然后添加一个方法到你的RPC接口:
Dummy dummy(Dummy d);
这个实现就是这样的:
Dummy dummy(Dummy d) { return d; }
asynchronous接口将有这样的:
void dummy(Dummy d, AsyncCallback< Dummy> callback);
GWT编译器会select它,因为Dummy
类引用了这些types,所以它将把它们包含在白名单中。
示例Dummy
类:
public class Dummy implements IsSerializable { private java.sql.Date d; }
白名单由GWT编译器生成,并包含由IsSerializable标记接口指定的所有条目。
要将一个types添加到列表中,只需确保该类实现了IsSerializable接口。
此外,为了序列化正常工作,类必须有一个默认的无参数构造函数(如果需要,构造函数可以是私有的)。 另外如果这个类是内部的,它必须被标记为静态的。
恕我直言,以最简单的方式访问白名单编程是创build一个类似这样的:
public class SerializableWhitelist implements IsSerializable { String[] dummy1; SomeOtherThingsIWishToSerialize dummy2; }
然后将它包含在.client
包中,并从RPC服务中引用(所以它被编译器分析)。
我找不到一个更好的方式来启用非参数化地图的传输,这显然是您有时需要创build更多通用服务的方法。
白名单由gwt编译器生成,并包含所有由IsSerializable标记接口指定的条目。
要将一个types添加到列表中,只需确保该类实现了IsSerializable接口。
– 安德烈
这可能是最简单的解决scheme。 唯一要记住的是所有你想要序列化的类都应该有“public,no-argument”构造函数,并且(取决于需求)成员字段的setter方法。
以确保所需的结果删除所有war/<app>/gwt/*.gwt.rpc
任何人谁会有同样的问题,并没有find以前的答案令人满意…
我正在使用GWT和GWTController,因为我使用了Spring,我修改了这个消息中描述的。 该消息解释了如何修改GrailsRemoteServiceServlet,但GWTController以相同的方式调用RPC.decodeRequest()和RPC.encodeResponseForSuccess()。
这是我正在使用的GWTController的最终版本:
/** * Used to instantiate GWT server in Spring context. * * Original version from <a href="http://docs.google.com/Doc?docid=dw2zgx2_25492p5qxfq&hl=en">this tutorial</a>. * * ...fixed to work as explained <a href="http://blog.js-development.com/2009/09/gwt-meets-spring.html">in this tutorial</a>. * * ...and then fixed to use StandardSerializationPolicy as explained in * <a href="http://markmail.org/message/k5j2vni6yzcokjsw">this message</a> to allow * using Serializable instead of IsSerializable in model. */ public class GWTController extends RemoteServiceServlet implements Controller, ServletContextAware { // Instance fields private RemoteService remoteService; private Class<? extends RemoteService> remoteServiceClass; private ServletContext servletContext; // Public methods /** * Call GWT's RemoteService doPost() method and return null. * * @param request * The current HTTP request * @param response * The current HTTP response * @return A ModelAndView to render, or null if handled directly * @throws Exception * In case of errors */ public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { doPost(request, response); return null; // response handled by GWT RPC over XmlHttpRequest } /** * Process the RPC request encoded into the payload string and return a string that encodes either the method return * or an exception thrown by it. * * @param payload * The RPC payload */ public String processCall(String payload) throws SerializationException { try { RPCRequest rpcRequest = RPC.decodeRequest(payload, this.remoteServiceClass, this); // delegate work to the spring injected service return RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest.getMethod(), rpcRequest.getParameters(), rpcRequest.getSerializationPolicy()); } catch (IncompatibleRemoteServiceException e) { return RPC.encodeResponseForFailure(null, e); } } /** * Setter for Spring injection of the GWT RemoteService object. * * @param RemoteService * The GWT RemoteService implementation that will be delegated to by the {@code GWTController}. */ public void setRemoteService(RemoteService remoteService) { this.remoteService = remoteService; this.remoteServiceClass = this.remoteService.getClass(); } @Override public ServletContext getServletContext() { return servletContext; } public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } }
我发现只是把它放在客户端软件包中,或者在虚拟服务接口中使用它是不够的,因为它似乎被系统优化了。
我发现创build一个派生自服务接口中已经使用的types的类并将其保存在客户端包中是最容易的。 没有别的需要。
public class GWTSerializableTypes extends SomeTypeInServiceInterface implements IsSerializable { Long l; Double d; private GWTSerializableTypes() {} }
我有这个问题,但最终追溯到我的Serializable对象中的一行代码:
Logger.getLogger(this.getClass().getCanonicalName()).log(Level.INFO, "Foo");
在发现exception之前没有其他投诉:
@Override protected void serialize(Object instance, String typeSignature) throws SerializationException { assert (instance != null); Class<?> clazz = getClassForSerialization(instance); try { serializationPolicy.validateSerialize(clazz); } catch (SerializationException e) { throw new SerializationException(e.getMessage() + ": instance = " + instance); } serializeImpl(instance, clazz); }
而堆栈跟踪的业务结束是:
com.google.gwt.user.client.rpc.SerializationException: Type 'net.your.class' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = net.your.class@9c7edce at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:619)