{"id":70,"date":"2012-07-06T19:18:54","date_gmt":"2012-07-06T18:18:54","guid":{"rendered":"http:\/\/mayeur.com\/wordpressfr\/?p=70"},"modified":"2012-07-06T19:59:49","modified_gmt":"2012-07-06T18:59:49","slug":"hijacking-gwt-rpc-serialization","status":"publish","type":"post","link":"https:\/\/mayeur.com\/wordpressfr\/hijacking-gwt-rpc-serialization\/","title":{"rendered":"Hijacking GWT-RPC serialization"},"content":{"rendered":"<p><a title=\"GWT\" href=\"https:\/\/developers.google.com\/web-toolkit\/\">GWT<\/a>, the Google Web Toolkit for client-side web programming, contains several client\/server communication frameworks. The \u00ab\u00a0traditional\u00a0\u00bb one is called GWT-RPC. GWT-RPC encapsulates the serialization\/deserialization of objects, and all the protocol plumbing.<\/p>\n<p>This is usually fine for most use cases, but I needed to explicitely serialize objects on the client side, and deserialize them on the server side.<\/p>\n<p><span style=\"text-decoration: underline;\">Use case<\/span> : I need to call a servlet from my GWT application, to open a document (generated server-side) in a new windows &#8211; using window.open()<\/p>\n<p><span style=\"text-decoration: underline;\">Solution<\/span> : create a standard GWT-RPC, declaring a \u00ab\u00a0fake\u00a0\u00bb method containing all the parameter types that we need to explicitly serialize. This will generate a correct GWT \u00ab\u00a0serialization policy\u00a0\u00bb. Then, server side, implement the GWT Service servlet, and override the <code>doGet<\/code> with your code.<\/p>\n<p>Create the standard GWT-RPC interfaces :<\/p>\n<p>The interface declares a fake method, with the only goal to allow the type we want to add in the serialization process (here, we have OutputTypeEnum, RepositoryServiceEnum and FBBaseFilterPagingLoadConfig).<\/p>\n<p><code>@RemoteServiceRelativePath(\"..\/fastbiz\/service\/recordUpload\")<br \/>\npublic interface RecordUploadService extends RemoteService {<br \/>\n&nbsp;\/\/ Fake<br \/>\n&nbsp;void fakeModel(OutputTypeEnum outputType, RepositoryServiceEnum repository, FBBaseFilterPagingLoadConfig config) throws FBException;<br \/>\n}<br \/>\n<\/code><br \/>\n&nbsp;<br \/>\n<code>public interface RecordUploadServiceAsync {<br \/>\n&nbsp;\/\/ Fake<br \/>\n&nbsp;void fakeModel(OutputTypeEnum outputType,RepositoryServiceEnum repository,FBBaseFilterPagingLoadConfig config, AsyncCallback&lt;Void&gt; callback);<br \/>\n}<br \/>\n<\/code><\/p>\n<ul type=\"disc\">\n<li>Client side :<\/li>\n<\/ul>\n<p><code>\/\/ Creating factory. Interface of any remote service can be used<br \/>\nSerializationStreamFactory factory = (SerializationStreamFactory) GWT.create(RecordUploadService.class);<\/p>\n<p>\/\/ Creating stream writer<br \/>\nSerializationStreamWriter writer = factory.createStreamWriter();<\/p>\n<p>\/\/ Serializing object<br \/>\nString serializedOutputType = null;<br \/>\nString serializedRepositoryIdentifier = null;<br \/>\ntry {<br \/>\n&nbsp;writer.writeObject(outputType);<br \/>\n&nbsp;\/\/ Getting serialized object content<br \/>\n&nbsp;serializedOutputType = URL.encodeQueryString(writer.toString());<\/p>\n<p>&nbsp;writer = factory.createStreamWriter();<br \/>\n&nbsp;writer.writeObject(listWindow.getRepositoryIdentifier());<\/p>\n<p>&nbsp;\/\/ Getting serialized object content<br \/>\n&nbsp;serializedRepositoryIdentifier = URL.encodeQueryString(writer.toString());<br \/>\n&nbsp;<br \/>\n&nbsp;writer = factory.createStreamWriter();<br \/>\n&nbsp;PagingLoadConfig config = listWindow.getConfig();<br \/>\n&nbsp;writer.writeObject(config);<br \/>\n} catch (SerializationException e) {<br \/>\n&nbsp;e.printStackTrace();<br \/>\n}<\/p>\n<p>\/\/ Getting serialized object content<br \/>\nString serializedConfig = URL.encodeQueryString(writer.toString());<br \/>\n&nbsp;<br \/>\nWindow.open(Upload.EXPORT_BASE_URL + \"?\" + Upload.PARAM_OUTPUT_TYPE + \"=\"<br \/>\n&nbsp;+ serializedOutputType + \"&amp;\"<br \/>\n&nbsp;+ Upload.PARAM_REPOSITORY_IDENTIFIER + \"=\"<br \/>\n&nbsp;+ serializedRepositoryIdentifier + \"&amp;\"<br \/>\n&nbsp;+ Upload.PARAM_PAGING_LOAD_CONFIG + \"=\"<br \/>\n&nbsp;+ serializedConfig, \"_blank\", null);<br \/>\n<\/code><br \/>\n&nbsp;<\/p>\n<ul type=\"disc\">\n<li>Server side :<\/li>\n<\/ul>\n<p>You implement the service interface, and override the GET method that way:<\/p>\n<p>&nbsp;<\/p>\n<p><code>public class RecordUploadServiceImpl extends RemoteServiceServlet implements com.fastbiz.client.service.RecordUploadService {<br \/>\n&nbsp;<br \/>\n&nbsp;private static final long serialVersionUID = 9107309941962048452L;<br \/>\n&nbsp;<br \/>\n&nbsp;@Override<br \/>\n&nbsp;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {<br \/>\n&nbsp;<br \/>\n&nbsp;\/\/ Ensure the thread-local data fields have been initialized<br \/>\n&nbsp;<br \/>\n&nbsp;try {<br \/>\n&nbsp;&nbsp;\/\/ Store the request &amp; response objects in thread-local storage.<br \/>\n&nbsp;&nbsp;synchronized (this) {<br \/>\n&nbsp;&nbsp;&nbsp;if (perThreadRequest == null) {<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;perThreadRequest = new ThreadLocal&lt;HttpServletRequest&gt;();<br \/>\n&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;&nbsp;if (perThreadResponse == null) {<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;perThreadResponse = new ThreadLocal&lt;HttpServletResponse&gt;();<br \/>\n&nbsp;&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;&nbsp;perThreadRequest.set(request);<br \/>\n&nbsp;&nbsp;&nbsp;perThreadResponse.set(response);<br \/>\n&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;processGet(request, response);<br \/>\n&nbsp;<br \/>\n&nbsp;} catch (Throwable e) {<br \/>\n&nbsp;&nbsp;\/\/ Give a subclass a chance to either handle the exception or<br \/>\n&nbsp;&nbsp;\/\/ rethrow it<br \/>\n&nbsp;&nbsp;doUnexpectedFailure(e);<br \/>\n&nbsp;} finally {<br \/>\n&nbsp;&nbsp;\/\/ null the thread-locals to avoid holding request\/response<br \/>\n&nbsp;&nbsp;perThreadRequest.set(null);<br \/>\n&nbsp;&nbsp;perThreadResponse.set(null);<br \/>\n&nbsp;}<br \/>\n&nbsp;<br \/>\n&nbsp;}<br \/>\n&nbsp;<br \/>\n&nbsp;protected void processGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {<br \/>\n&nbsp;&nbsp;if (Upload.TYPE_LIST.equals(origin)) {<br \/>\n&nbsp;<br \/>\n&nbsp;&nbsp;\/\/ Initializing stream reader<br \/>\n&nbsp;&nbsp;ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(<br \/>\n&nbsp;&nbsp;Thread.currentThread().getContextClassLoader(), this);<br \/>\n&nbsp;&nbsp;RepositoryServiceEnum recordType;<br \/>\n&nbsp;&nbsp;FilterPagingLoadConfig config;<br \/>\n&nbsp;&nbsp;OutputTypeEnum outputType;<br \/>\n&nbsp;&nbsp;try {<br \/>\n&nbsp;&nbsp;&nbsp;\/\/ Filling stream reader with data<br \/>\n&nbsp;&nbsp;&nbsp;streamReader.prepareToRead(request.getParameter(Upload.PARAM_OUTPUT_TYPE));<br \/>\n&nbsp;&nbsp;&nbsp;\/\/ Reading deserialized object from the stream<br \/>\n&nbsp;&nbsp;&nbsp;outputType = (OutputTypeEnum) streamReader.readObject();<br \/>\n&nbsp;&nbsp;} catch (SerializationException e1) {<br \/>\n&nbsp;&nbsp;&nbsp;throw new ServletException(\"Unable to deserialize parameters\",<br \/>\n&nbsp;&nbsp;&nbsp;new FBException(e1));<br \/>\n&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;try {<br \/>\n&nbsp;&nbsp;&nbsp;\/\/ Filling stream reader with data<br \/>\n&nbsp;&nbsp;&nbsp;streamReader.prepareToRead(request.getParameter(Upload.PARAM_REPOSITORY_IDENTIFIER));<br \/>\n&nbsp;&nbsp;&nbsp;\/\/ Reading deserialized object from the stream<br \/>\n&nbsp;&nbsp;&nbsp;recordType = (RepositoryServiceEnum) streamReader.readObject();<br \/>\n&nbsp;&nbsp;} catch (SerializationException e1) {<br \/>\n&nbsp;&nbsp;&nbsp;throw new ServletException(\"Unable to deserialize parameters\",<br \/>\n&nbsp;&nbsp;&nbsp;new FBException(e1));<br \/>\n&nbsp;&nbsp;}<br \/>\n&nbsp;&nbsp;try {<br \/>\n&nbsp;&nbsp;&nbsp;\/\/ Filling stream reader with data<br \/>\n&nbsp;&nbsp;&nbsp;streamReader.prepareToRead(request.getParameter(Upload.PARAM_PAGING_LOAD_CONFIG));<br \/>\n&nbsp;&nbsp;&nbsp;\/\/ Reading deserialized object from the stream<br \/>\n&nbsp;&nbsp;&nbsp;config = (FilterPagingLoadConfig) streamReader.readObject();<br \/>\n&nbsp;&nbsp;} catch (SerializationException e1) {<br \/>\n&nbsp;&nbsp;&nbsp;throw new ServletException(\"Unable to deserialize parameters\",<br \/>\n&nbsp;&nbsp;&nbsp;new FBException(e1));<br \/>\n&nbsp;&nbsp;}<br \/>\n&nbsp;<br \/>\n&nbsp;}<br \/>\n&nbsp;<br \/>\n}<br \/>\n&nbsp;<br \/>\n&nbsp;@Override<br \/>\n&nbsp;public void fakeBaseModel(OutputTypeEnum outputType, RepositoryServiceEnum repository, FBBaseFilterPagingLoadConfig config) throws FBException {<br \/>\n&nbsp;}<br \/>\n&nbsp;<br \/>\n}<br \/>\n<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>GWT, the Google Web Toolkit for client-side web programming, contains several client\/server communication frameworks. The \u00ab\u00a0traditional\u00a0\u00bb one is called GWT-RPC. GWT-RPC encapsulates the serialization\/deserialization of objects, and all the protocol plumbing. This is usually fine for most use cases, but I needed to explicitely serialize objects on the client side, and deserialize them on the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[10,5],"tags":[],"class_list":["post-70","post","type-post","status-publish","format-standard","hentry","category-gwt","category-java"],"_links":{"self":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts\/70","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/comments?post=70"}],"version-history":[{"count":11,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts\/70\/revisions"}],"predecessor-version":[{"id":83,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/posts\/70\/revisions\/83"}],"wp:attachment":[{"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/media?parent=70"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/categories?post=70"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mayeur.com\/wordpressfr\/wp-json\/wp\/v2\/tags?post=70"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}