<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1170850255680179983</id><updated>2011-07-28T13:30:52.501+02:00</updated><category term='Javascript Native Interfaces'/><category term='JSNI'/><category term='GWT'/><category term='Google Wave Gadget'/><category term='JavaScript'/><category term='Java'/><category term='Google Web Toolkit'/><category term='tutorial'/><title type='text'>Dendryt - Agile Development Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dendrytsoft.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170850255680179983/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dendrytsoft.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Michał R.</name><uri>http://www.blogger.com/profile/08538685060055015220</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_HZAPwfHe1MM/SuO2dltlYsI/AAAAAAAAABI/pkE3vPGRASA/S220/AIbEiAIAAABDCKa5jO69va2CGSILdmNhcmRfcGhvdG8qKDQ4ODE5Zjk0MjIzYWE0Nzk1MjE2NTkyNWZkOWI3OTZiZTlkMzYzODgwAapSwV0vKvm9aJ6sHLRa6S2K3x3E.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1170850255680179983.post-7924146861011796687</id><published>2009-11-10T13:47:00.004+01:00</published><updated>2009-11-10T13:59:27.187+01:00</updated><title type='text'>Errors happen</title><content type='html'>&lt;span class="Apple-style-span"    style="font-family:'trebuchet ms', verdana, arial, sans-serif;font-size:100%;color:#333333;"&gt;&lt;span class="Apple-style-span"  style="border-collapse: collapse;  line-height: 18px; -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px;font-size:13px;"&gt;&lt;div&gt;I have noticed a few DatastoreTimeoutException in logs of my application hosted on Google App Engine. It looks like below:&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;pre name="code" class="xml:nocontrols"&gt;&lt;br /&gt;com.google.appengine.api.datastore.DatastoreTimeoutException: Unknown&lt;br /&gt;com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:42)&lt;br /&gt;com.google.appengine.api.datastore.DatastoreApiHelper.makeSyncCall(DatastoreApiHelper.java:60)&lt;br /&gt;com.google.appengine.api.datastore.DatastoreServiceImpl$PreparedQueryImpl.runQuery(DatastoreServiceImpl.java:346)&lt;br /&gt;com.google.appengine.api.datastore.DatastoreServiceImpl$PreparedQueryImpl.access$100(DatastoreServiceImpl.java:272)&lt;br /&gt;com.google.appengine.api.datastore.DatastoreServiceImpl$PreparedQueryImpl$1.iterator(DatastoreServiceImpl.java:306)&lt;br /&gt;org.datanucleus.store.appengine.query.RuntimeExceptionWrappingIterable.iterator(RuntimeExceptionWrappingIterable.java:42)&lt;br /&gt;org.datanucleus.store.appengine.query.StreamingQueryResult.(StreamingQueryResult.java:77)&lt;br /&gt;org.datanucleus.store.appengine.query.DatastoreQuery.newStreamingQueryResultForEntities(DatastoreQuery.java:324)&lt;br /&gt;org.datanucleus.store.appengine.query.DatastoreQuery.fulfillEntityQuery(DatastoreQuery.java:310)&lt;br /&gt;org.datanucleus.store.appengine.query.DatastoreQuery.performExecute(DatastoreQuery.java:242)&lt;br /&gt;org.datanucleus.store.appengine.query.JDOQLQuery.performExecute(JDOQLQuery.java:84)&lt;br /&gt;org.datanucleus.store.query.Query.executeQuery(Query.java:1489)&lt;br /&gt;org.datanucleus.store.query.Query.executeWithArray(Query.java:1371)&lt;br /&gt;org.datanucleus.store.query.Query.execute(Query.java:1344)&lt;br /&gt;org.datanucleus.jdo.JDOQuery.execute(JDOQuery.java:221)&lt;br /&gt;com.dendrytsoft.chat.server.MessageDAO.readAfterDate(MessageDAO.java:69)&lt;br /&gt;com.dendrytsoft.chat.server.GreetingServiceImpl.getAllMessages(GreetingServiceImpl.java:38)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I found a hint about this from &lt;a href="http://groups.google.com/group/google-appengine-java/browse_thread/thread/31e1f21cf5deff07/ced822ff4537b34d?lnk=gst&amp;amp;q=DatastoreTimeoutException#ced822ff4537b34d"&gt;Jason Cooper(Google)&lt;/a&gt;:&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;The majority of exceptions are thrown because two or more requests come in at the same time that attempt to write to a single entity or entity group. This results in write contention, and an exception may be thrown if a given request can't complete its write within the deadline. So, during your design stage, be sure to identify areas where this may be a problem and mitigate as much as possible. You can do this by keeping your entity groups small and&lt;/div&gt;&lt;div&gt;sharding single entities that may updated a lot, such as a global counter: http://code.google.com/intl/pl/appengine/articles/sharding_counters.html&lt;/div&gt;&lt;div&gt;Due to the distributed nature of App Engine's datastore, these exceptions will happen from time to time; &lt;b&gt;in practice, this effects between 0.1 and 0.2 percent of all datastore operations&lt;/b&gt;, and we are always working to make this percentage even lower.&lt;b&gt; For critical datastore operations, you should continue to catch the exception so you can provide a custom error if necessary or perform your own retries.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;[...]&lt;/div&gt;&lt;div&gt;I would recommending two or three times [execute retry] maximum before showing the user an error message.&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have implemented retry when DatastoreTimeoutException occures and it looks that works great now.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There is an interesting speach about quality of applications hosted on App Engine:&lt;a href="http://sites.google.com/site/io/best-practices---building-a-production-quality-application-on-google-app-engine"&gt; Best Practices - Building a Production Quality Application on Google App Engine.&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Ken Ashcraft diveded problems that could happen on few clasess:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;• Out-of-memory&lt;/div&gt;&lt;div&gt;• DeadlineExceeded&lt;/div&gt;&lt;div&gt;• OverQuotaError&lt;/div&gt;&lt;div&gt;• Server crash&lt;/div&gt;&lt;div&gt;• Datastore crash&lt;/div&gt;&lt;div&gt;• Identical entity already exists&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I've recently read Coders At Work (great book), where Ken Thompson wrote that he is guessing that about 50 percent of code in Google infrastructure is connected with handling exceptions/errors/failures that occures during runtime. &lt;/div&gt;&lt;div&gt;I have some experience with real-time distributed telecommunication systems. I haven't thinking before how many percent of this systems code is the special situations handling mechanism. But surprisingly, realy: it could be even an half. It looks like we should be more aware of underlying distributed infrastructure while developing applications on top od GAE.&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170850255680179983-7924146861011796687?l=dendrytsoft.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dendrytsoft.blogspot.com/feeds/7924146861011796687/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://dendrytsoft.blogspot.com/2009/11/errors-happen.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170850255680179983/posts/default/7924146861011796687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170850255680179983/posts/default/7924146861011796687'/><link rel='alternate' type='text/html' href='http://dendrytsoft.blogspot.com/2009/11/errors-happen.html' title='Errors happen'/><author><name>Michał R.</name><uri>http://www.blogger.com/profile/08538685060055015220</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_HZAPwfHe1MM/SuO2dltlYsI/AAAAAAAAABI/pkE3vPGRASA/S220/AIbEiAIAAABDCKa5jO69va2CGSILdmNhcmRfcGhvdG8qKDQ4ODE5Zjk0MjIzYWE0Nzk1MjE2NTkyNWZkOWI3OTZiZTlkMzYzODgwAapSwV0vKvm9aJ6sHLRa6S2K3x3E.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1170850255680179983.post-3703890535010608077</id><published>2009-10-25T00:16:00.023+02:00</published><updated>2009-11-02T01:56:52.313+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='GWT'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Wave Gadget'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Web Toolkit'/><category scheme='http://www.blogger.com/atom/ns#' term='JSNI'/><category scheme='http://www.blogger.com/atom/ns#' term='Javascript Native Interfaces'/><title type='text'>Building a Google Wave Gadget with GWT (Java + JavaScript)</title><content type='html'>&lt;div style="text-align: left;"&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;I am very interested in Google's products for developers. I have been doing a few projects in Google Web Toolkit since version 1.5 when its developement environment and support was rather poor. I must agree that now there are many mature tools, good eclipse plug-in, many JS API wrappers (for example: &lt;a href="http://code.google.com/p/gwt-google-apis/"&gt;gwt-google-apis&lt;/a&gt;), full Java development stack: &lt;a href="http://code.google.com/intl/pl-PL/appengine/"&gt;Google App Engine&lt;/a&gt;(great and free for people starting with GWT)  and nice community around this technology. But in this post I will focus on new Google product: Wave and I will shortly describe how to write a Gadget for it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt; &lt;/span&gt;0.Introduction&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;I will write a gadget that will count how many pizzas should I order for the evening party. Adding all party's guests to the wave I can enable them to vote, how much they will eat, so I will be able to estimate the total number of pizzas I should order ;) That's tiny example, but it illustrates the way to implement a Wave Gadget.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After adding the gadget to a wave it will look like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://4.bp.blogspot.com/_HZAPwfHe1MM/SuOrNDN-EUI/AAAAAAAAAAk/uPuuGUNHQvU/s320/1.bmp" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 308px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5396345019210535234" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After voting: 1pizza and adding two more participants to the wave, it will look like this:&lt;/div&gt;&lt;div&gt;&lt;div style="text-align: center;"&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_HZAPwfHe1MM/SuOtdREsHoI/AAAAAAAAAA0/G7EQwniiLAk/s1600-h/3.bmp"&gt;&lt;img src="http://3.bp.blogspot.com/_HZAPwfHe1MM/SuOtdREsHoI/AAAAAAAAAA0/G7EQwniiLAk/s320/3.bmp" border="0" alt="" id="BLOGGER_PHOTO_ID_5396347496830869122" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 269px; height: 320px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;After they vote, it will look like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_HZAPwfHe1MM/SuOuF9l09mI/AAAAAAAAAA8/a-rnVczfVYc/s1600-h/4.bmp"&gt;&lt;img src="http://1.bp.blogspot.com/_HZAPwfHe1MM/SuOuF9l09mI/AAAAAAAAAA8/a-rnVczfVYc/s320/4.bmp" border="0" alt="" id="BLOGGER_PHOTO_ID_5396348195975788130" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 262px; height: 320px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;So that is gadget that is described below. Lets code it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;1.Configuration&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;First of all you need to add a line below to your GWT modul descriptor. In my case its DendrytGadget.gwt.xml.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;pre name="code" class="xml:nocontrols"&gt;&amp;lt;script src="http://wave-api.appspot.com/public/wave.js"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;Now you can start creating your gadget's main class. In my case it is DendrytGadget class. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;@Gadget.ModulePrefs(title = "DendrytGadget", height=400, scrolling=true)&lt;br /&gt;@Gadget.InjectModulePrefs(files = {"ModulePrefsRpc", "ModulePrefsDynamicHeight"})&lt;br /&gt;public class DendrytGadget extends Gadget&amp;lt;UserPreferences&amp;gt;{&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You can set preferences connected with the gadget using Gadget. Annotation Gadget.InjectModulePrefs enables developers to define hand-written content for the ModulePrefs section of the gadget XML manifset file. Each Require XML node should be holded in separate file. In this case we have two files ModulePrefsDynamicHeight:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="xml:nocontrols"&gt;&amp;lt;Require feature="dynamic-height"/&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Georgia, serif; font-size: 16px; white-space: normal; "&gt;and ModulePrefsRpc:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: monospace; font-size: 13px; white-space: pre; "&gt;&lt;pre name="code" class="xml:nocontrols"&gt;&amp;lt;Require feature="rpc"/&amp;gt;&lt;/pre&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;2.Gadget's initialization&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;Our gadget class must extends Gadget&amp;lt;UserPreferences&amp;gt; abstract class and implement one abstract method (below). This method will be called after all of the feature initialization hooks have been called.&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;&lt;br /&gt;protected abstract void init(T preferences);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We will implement it in the way below:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;@Override&lt;br /&gt;protected void init(UserPreferences userPreferences) {&lt;br /&gt;    instance = this;&lt;br /&gt;    RootPanel.get().add(generateAmountPanel());&lt;br /&gt;    WaveWrapper.registerStateChangeCallback();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Be careful at init()-method body:&lt;/b&gt; non-catched RuntimeExceptions make your gadget works wrong. Its GUI may be built, but every wave-Object's method will return null. The same problem occurs when you call $wnd.wave.getState().get() from the init()-method.  Method getState() will return null and RunTimeException will be thrown:&lt;div&gt;&lt;pre name="code" class="xml:nocontrols"&gt;com.google.gwt.core.client.JavaScriptException: (TypeError): $wnd.wave.getState() is null&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;That bug could be quite hard to find when you are starting Wave Gadget development. So be aware  :) It looks that the good idea here is to surround every native JSNI method call with a try-catch block.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;3.Registering state-change callback&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;Variable instance is DendrytGadget's static field. I use this workaround to set state-change callback via &lt;a href="http://code.google.com/webtoolkit/documentation/com.google.gwt.doc.DeveloperGuide.JavaScriptNativeInterface.html"&gt;JSNI &lt;/a&gt;code (below). This native JS code registers  static DendrytGadget.stateUpdated() method as a callback. &lt;i&gt;Our gadget will be interested in the state of amount of pizzas that each user wants to order.&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;&lt;br /&gt;public native static void registerStateChangeCallback() /*-{&lt;br /&gt;    var wave = $wnd.wave;&lt;br /&gt;    if (wave) {&lt;br /&gt;        wave.setStateCallback(@com.dendrytsoft.wave.dendrytgadget.client.DendrytGadget::stateUpdated());&lt;br /&gt;    }&lt;br /&gt;}-*/;&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt; &lt;br /&gt;&lt;br /&gt;&lt;/span&gt;4.Generation GUI&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;generateAmountPanel()-method generates Gadget's GUI using standard GWT widgets. It creates 4 RadioButton's ("1 pizza, 2 pizzas..."), HTML object(showing choice for each user) and one Label("To sum it up..." - total number of pizzas to make order for). Each RadioButton has implemented ClickListener that calls setChoice()-method (below) that persist the pair (viewerId, choice)  in Wave's persistency key-value state map. Only value that we want to persist and share with other Wave's clients is obviously our choice - &lt;i&gt;number of pizzas that we will eat&lt;/i&gt;.&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:monospace;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre;font-size:13px;"&gt;&lt;span class="Apple-style-span"   style="font-family:Georgia, serif;font-size:130%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: normal;font-size:16px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;public void setChoice(int choice){&lt;br /&gt;    Map&amp;lt;String, String&amp;gt; map = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt;    map.put(WaveWrapper.getViewerId(), String.valueOf(choice));&lt;br /&gt;    WaveWrapper.submitDelta(map);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt; &lt;/span&gt;5.Wave's state persistency (WRITE)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;Persistency write handling methods should be implemented in the way below:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:monospace;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre;font-size:13px;"&gt;&lt;span class="Apple-style-span"   style="font-family:Georgia, serif;font-size:130%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: normal;font-size:16px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;/**&lt;br /&gt;* Updates the state object with delta, which is a map of key-value pairs representing an update&lt;br /&gt;*/&lt;br /&gt;public static void submitDelta(Map&amp;lt;String, String&amp;gt; delta) {&lt;br /&gt;    JavaScriptObject map = JavaScriptObject.createObject();&lt;br /&gt;    for (Map.Entry&amp;lt;String, String&amp;gt; entry : delta.entrySet()) {&lt;br /&gt;        addEntryToMap(map, entry.getKey(), entry.getValue());&lt;br /&gt;    }&lt;br /&gt;    internalSubmitDelta(map);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private static native JavaScriptObject addEntryToMap(JavaScriptObject map, String key, String value) /*-{&lt;br /&gt;    map[key] = value;&lt;br /&gt;    return map;&lt;br /&gt;}-*/;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Updates the state object with delta, which is a map of key-value pairs representing an update&lt;br /&gt;* (internal call)&lt;br /&gt;*/&lt;br /&gt;private static native void internalSubmitDelta(JavaScriptObject delta) /*-{&lt;br /&gt;    $wnd.wave.getState().submitDelta(delta);&lt;br /&gt;}-*/;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It could be written with less JSNI-fun using com.google.gwt.event.dom.client.PrivateMap class that provides lightweight map implementation with API for both Java and JavaScript, but package is protected due to non-final API. It shows us that clearly that GWT is not yet ready and there are situations where you must use Javascript Native Interfaces.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt; &lt;/span&gt;6.Wave's state persistency(READ)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;So, that was about changing wave's state. Now lets talk about reading state. Every time the state changes (&lt;i&gt;one of the participants make a decision about number of pizzas he/she want to order&lt;/i&gt;), out callback method is called.&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:monospace;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre;font-size:13px;"&gt;&lt;span class="Apple-style-span"   style="font-family:Georgia, serif;font-size:130%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: normal;font-size:16px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;public static void stateUpdated() {&lt;br /&gt;    // ...&lt;br /&gt;    StringBuilder sb = new StringBuilder();&lt;br /&gt;    int counter = 0;&lt;br /&gt;    for(Participant p : WaveWrapper.getParticipants()){&lt;br /&gt;        sb.append(p.getParticipantsInHtml());&lt;br /&gt;        int choice = Integer.parseInt(instance.getUserChoice(p.getId()));&lt;br /&gt;        sb.append(" : " + choice);&lt;br /&gt;        sb.append("&amp;lt;br/&amp;gt;");&lt;br /&gt;        counter += choice;&lt;br /&gt;    }&lt;br /&gt;    String s = counter + ((counter == 1) ? UNIT : UNIT + "s");&lt;br /&gt;    instance.label.setText("To sum it up, we will need: " + s);&lt;br /&gt;    instance.htmlField.setHTML(sb.toString());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public String getUserChoice(String userID){&lt;br /&gt;    String s = WaveWrapper.get(userID);&lt;br /&gt;    return (s != null) ? s : "0";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Get a value for the given key from Gadget persistentency key-value map&lt;br /&gt;*/&lt;br /&gt;public static native String get(String key) /*-{&lt;br /&gt;    return $wnd.wave.getState().get(key);&lt;br /&gt;}-*/;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;7.Mapping of Javascript object to Java object&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;So, as you see it is implemented in the same way as the writing changes was. Next interesting thing is mapping of Javascript object to Java object. It is easy. As you see in stateUpdated()-method, there was WaveWrapper.getParticipants() JSNI-method call which returns array of Participant Javascript objects. To use it in Java code we need to implement class that extends JavaScriptObject class. Access to all its fields/methods via JSNI-methods is easy (see below).&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:monospace;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre;font-size:13px;"&gt;&lt;span class="Apple-style-span"   style="font-family:Georgia, serif;font-size:130%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: normal;font-size:16px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre name="code" class="java:nocontrols"&gt;public static native Participant[] getParticipants() /*-{&lt;br /&gt;    return $wnd.wave.getParticipants();&lt;br /&gt;}-*/;&lt;br /&gt;&lt;br /&gt;public class Participant extends JavaScriptObject {&lt;br /&gt;    protected Participant(){}&lt;br /&gt;    public final native String getId() /*-{ return this.getId(); }-*/;&lt;br /&gt;    public final native String getThumbnailUrl() /*-{ return this.getThumbnailUrl(); }-*/;&lt;br /&gt;    public final native String getDisplayName() /*-{ return this.getDisplayName(); }-*/;&lt;br /&gt;&lt;br /&gt;    public final String getParticipantsInHtml(){&lt;br /&gt;        return "&amp;lt;img  src=\"" + getThumbnailUrl() + "\" height=\"30\" width=\"30\" /&amp;gt;" + getDisplayName();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;And I think that's all what you need to know to develop Wave Gadget. So that's all, thanks for attention :) Sourcecode is available &lt;a href="http://files.dendrytsoft.com/dendrytgadget/DendrytGadget.7z"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;BTW I've just found &lt;a href="http://code.google.com/p/cobogwave/"&gt;cobogwave&lt;/a&gt; project that is quite interesting. I haven't used it yet. It looks it wraps whole &lt;a href="http://code.google.com/intl/pl-PL/apis/wave/extensions/gadgets/reference.html"&gt;Google Wave Gadgets JS API&lt;/a&gt;, so using it could help you write less JSNI code.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;Michał Radziwon&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1170850255680179983-3703890535010608077?l=dendrytsoft.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dendrytsoft.blogspot.com/feeds/3703890535010608077/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://dendrytsoft.blogspot.com/2009/10/building-google-wave-gadget-with-gwt.html#comment-form' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1170850255680179983/posts/default/3703890535010608077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1170850255680179983/posts/default/3703890535010608077'/><link rel='alternate' type='text/html' href='http://dendrytsoft.blogspot.com/2009/10/building-google-wave-gadget-with-gwt.html' title='Building a Google Wave Gadget with GWT (Java + JavaScript)'/><author><name>Michał R.</name><uri>http://www.blogger.com/profile/08538685060055015220</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_HZAPwfHe1MM/SuO2dltlYsI/AAAAAAAAABI/pkE3vPGRASA/S220/AIbEiAIAAABDCKa5jO69va2CGSILdmNhcmRfcGhvdG8qKDQ4ODE5Zjk0MjIzYWE0Nzk1MjE2NTkyNWZkOWI3OTZiZTlkMzYzODgwAapSwV0vKvm9aJ6sHLRa6S2K3x3E.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_HZAPwfHe1MM/SuOrNDN-EUI/AAAAAAAAAAk/uPuuGUNHQvU/s72-c/1.bmp' height='72' width='72'/><thr:total>1</thr:total></entry></feed>
