GWTのシリアライザーはJava.io.Serializable
のサポートに制限がありますが、セキュリティ上の理由から、GWTがサポートするタイプのホワイトリストがあります。私が見つけたドキュメント、たとえば this FAQ entry は、シリアル化するすべての型が「シリアル化ポリシーのホワイトリストに含まれている必要がある」こと、およびリストはコンパイル時に生成されますが、コンパイラがホワイトリストに何を入れるかを決定する方法については説明していません。
生成されたリストには、Java.lang.String
やJava.util.HashMap
など、標準ライブラリの一部である多くのタイプが含まれています。 Serializable
インターフェースを実装するJava.sql.Date
をシリアル化しようとするとエラーが発生しますが、ホワイトリストにはありません。このタイプをリストに追加するにはどうすればよいですか?
サービスインターフェースに含める特定のタイプと、それらが参照するタイプは、Java.io.Serializableを実装している限り、自動的にホワイトリストに登録されます。例:
public String getStringForDates(ArrayList<Java.util.Date> dates);
結果として、ArrayListとDateの両方がホワイトリストに含まれます。
特定の型の代わりにJava.lang.Objectを使用すると、さらに難しくなります。
public Object getObjectForString(String str);
コンパイラはホワイトリストに何を登録するのか分からないからです。その場合、オブジェクトがサービスインターフェイスのどこでも参照されていない場合は、IsSerializableインターフェイスで明示的にマークする必要があります。そうしないと、RPCメカニズムを介してオブジェクトを渡すことができません。
回避策があります。シリアライゼーションに含めるすべてのタイプのメンバーフィールドを持つ新しいDummy
クラスを定義します。次に、RPCインターフェイスにメソッドを追加します。
Dummy dummy(Dummy d);
実装はこれだけです:
Dummy dummy(Dummy d) { return d; }
そして、非同期インターフェースはこれを持っています:
void dummy(Dummy d, AsyncCallback< Dummy> callback);
GWTコンパイラーはこれを取得し、Dummy
クラスはそれらのタイプを参照するため、ホワイトリストに含まれます。
Dummy
クラスの例:
public class Dummy implements IsSerializable {
private Java.sql.Date d;
}
ホワイトリストはGWTコンパイラーによって生成され、IsSerializableマーカーインターフェイスによって指定されたすべてのエントリを含みます。
リストに型を追加するには、クラスがIsSerializableインターフェイスを実装していることを確認する必要があります。
さらに、シリアル化が正しく機能するためには、クラスにデフォルトの引数なしコンストラクターが必要です(コンストラクターは必要に応じてプライベートにすることができます)。また、クラスが内部の場合は、静的としてマークする必要があります。
プログラムでホワイトリストにアクセスする最も簡単な方法は、次のようなクラスを作成することです。
public class SerializableWhitelist implements IsSerializable {
String[] dummy1;
SomeOtherThingsIWishToSerialize dummy2;
}
次に、それを.client
パッケージに含め、RPCサービスからの参照を含めます(コンパイラーによって分析されます)。
パラメータ化されていないマップの転送を有効にするより良い方法を見つけることができませんでした。これは明らかに、より一般的なサービスを作成するために必要な場合があることです...
望ましい結果を確実にするには、すべてを削除してくださいwar/<app>/gwt/*.gwt.rpc
ホワイトリストはgwtコンパイラーによって生成され、IsSerializableマーカーインターフェイスによって指定されたすべてのエントリを含みます。
リストに型を追加するには、クラスがIsSerializableインターフェイスを実装していることを確認する必要があります。
-アンドレイジ
これがおそらく最も簡単な解決策です。これについて覚えておくべき唯一のことは、シリアル化するすべてのクラスには、「パブリックで引数のない」コンストラクターと、メンバーフィールドの(要件によっては)セッターメソッドが必要です。
同じ質問をし、以前の答えが満足のいくものではない人に...
説明したように変更したSpringを使用しているため、GWTControllerでGWTを使用しています このメッセージで 。メッセージは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;
}
}
私はこの問題を抱えていましたが、問題を追跡してSerializableオブジェクトのコード行に戻しました。
Logger.getLogger(this.getClass().getCanonicalName()).log(Level.INFO, "Foo");
例外が発生する前に他の不満はありませんでした:
@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)
クライアントパッケージに入れるか、ダミーサービスインターフェイスで使用するだけでは、システムによって最適化されているように見えたため、十分ではないことがわかりました。
サービスインターフェイスで既に使用されている型の1つから派生したクラスを作成し、クライアントパッケージに貼り付けるのが最も簡単であることがわかりました。他には何も必要ありません。
public class GWTSerializableTypes extends SomeTypeInServiceInterface implements IsSerializable {
Long l;
Double d;
private GWTSerializableTypes() {}
}