Here is the last post about the yoctopuce project. We have shown how to create a development environment from scratch, how to use the yoctopuce devices and how to store them in a Google app engine.
The next blog will describe how to a put a photogrammetry project in place that you can find here.
Rechercher dans ce blog
dimanche 6 mai 2012
mercredi 2 mai 2012
16. Google app engine
Now that we have a platform web service working, we will make the back-end in order to return and store the yoctopuce output in the database. this part does not follow the traditional design patterns but instead show a PoC.
What is Google app engine? Google app engine is a framework provided by ... (you can guess!).
"Google App Engine lets you run web applications on Google's infrastructure. App Engine applications are easy to build, easy to maintain, and easy to scale as your traffic and data storage needs grow. With App Engine, there are no servers to maintain: You just upload your application, and it's ready to serve your users."
To set google app engine (GAE) with Intellij Idea, the facet must be enabled during the project creation. It will create a default project structure with a particular file called appengine-web.xml. In particular the file will contain:
In this configuration file, the application and the version are used by the google infrastructure to determine the current version and application.
In order to use the database, we must describe how the database is accessed. This is described in META-INF/jdoconfig.xml.
As soon as the Google app engine SDK is specified in Intellij Idea, you can run and debug your application using the standard UI.
This interface describes functions to list objects from database and add objects in the database. The annotation will help GAE to understand which URL must be managed by this service.
It is very important to understand that this interface (created on the client side) is used by the server. The interface used by the client will use asynchronous functions. Fortunately Intellij Idea creates such functions automatically, here is the interface that is created automatically:
On the client side, the code to call the service is the following:
This will automatically create a AsyncService class that can be used out of the box.
On the server side, we implement the WorldMapService:
What do we do here:
That's all for the code! Everything is now ready to put the project in the google app engine infrastructure.
And that's all!!!
Warning: Do not store the GAE password in Intellij Idea and the Intellij Idea configuration file on GIT: it would mean your password (encrypted) will be stored in GIT.
What is Google app engine? Google app engine is a framework provided by ... (you can guess!).
"Google App Engine lets you run web applications on Google's infrastructure. App Engine applications are easy to build, easy to maintain, and easy to scale as your traffic and data storage needs grow. With App Engine, there are no servers to maintain: You just upload your application, and it's ready to serve your users."
Google App engine configuration
To set google app engine (GAE) with Intellij Idea, the facet must be enabled during the project creation. It will create a default project structure with a particular file called appengine-web.xml. In particular the file will contain:
16 17 <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> 18 <application>yocto-meteo</application> 19 <version>3</version> 20 <static-files>
In this configuration file, the application and the version are used by the google infrastructure to determine the current version and application.
In order to use the database, we must describe how the database is accessed. This is described in META-INF/jdoconfig.xml.
13 14 <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" 15 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 16 xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> 17 18 <persistence-manager-factory name="transactions-optional"> 19 <property name="javax.jdo.PersistenceManagerFactoryClass" 20 value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/> 21 <property name="javax.jdo.option.ConnectionURL" value="appengine"/> 22 <property name="javax.jdo.option.NontransactionalRead" value="true"/> 23 <property name="javax.jdo.option.NontransactionalWrite" value="true"/> 24 <property name="javax.jdo.option.RetainValues" value="true"/> 25 <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> 26 </persistence-manager-factory> 27 </jdoconfig> 28
As soon as the Google app engine SDK is specified in Intellij Idea, you can run and debug your application using the standard UI.
Create the communication layer
For the communication layer, we would like to store the output of the YoctoHub and its localization in the database. For this purpose, Google provides a lot of help. The first step is to create an interface that specifies the functions that the server will support:31 32 @RemoteServiceRelativePath("worldmap") 33 public interface WorldMapService extends RemoteService { 34 35 public void addMeteo(DataMeteo dataMeteo); 36 37 public List<DataMeteo> listMeteos(); 38 39 public void addHub(DataHub dataHub); 40 41 public List<DataHub> listHubs(); 42 43 public List<DataColor> listColors(); 44 45 public void addColor(DataColor color); 46 } 47
This interface describes functions to list objects from database and add objects in the database. The annotation will help GAE to understand which URL must be managed by this service.
It is very important to understand that this interface (created on the client side) is used by the server. The interface used by the client will use asynchronous functions. Fortunately Intellij Idea creates such functions automatically, here is the interface that is created automatically:
24 25 public interface WorldMapServiceAsync { 26 27 void addMeteo(DataMeteo dataMeteo, AsyncCallback<Void> async); 28 29 void listMeteos(AsyncCallback<List<DataMeteo>> async); 30 31 void listHubs(AsyncCallback<List<DataHub>> async); 32 33 void addHub(DataHub dataHub, AsyncCallback<Void> async); 34 35 void listColors(AsyncCallback<List<DataColor>> async); 36 37 void addColor(DataColor color, AsyncCallback<Void> async); 38 }
On the client side, the code to call the service is the following:
private final WorldMapServiceAsync worldMapService = GWT.create(WorldMapService.class);
This will automatically create a AsyncService class that can be used out of the box.
On the server side, we implement the WorldMapService:
39 40 public class WorldMapServiceImpl extends RemoteServiceServlet implements WorldMapService { 41 42 43 private final Logger logger = Logger.getLogger(this.getClass().getName()); 44 45 private static final PersistenceManagerFactory PMF = 46 JDOHelper.getPersistenceManagerFactory("transactions-optional"); 47 private DAO<DataMeteo> meteoDAO; 48 private DAO<DataHub> hubDAO; 49 private DAO<DataColor> colorDAO; 50 51 52 public WorldMapServiceImpl() { 53 meteoDAO = new DAO<DataMeteo>(); 54 hubDAO = new DAO<DataHub>(); 55 colorDAO = new DAO<DataColor>(); 56 } 57 58 // 59 public void addMeteo(DataMeteo dataMeteo) { 60 logger.info("Adding a new meteo in the Database"); 61 meteoDAO.add(dataMeteo); 62 } 63 64 public List<DataMeteo> listMeteos() { 65 logger.info("Listing all the meteos"); 66 return meteoDAO.list(DataMeteo.class); 67 } 68 69 // 70 private class DAO<T> { 71 public List<T> list(Class clazz) { 72 PersistenceManager pm = getPersistenceManager(); 73 List<T> list; 74 try { 75 Query q = pm.newQuery(clazz); 76 list = (List<T>) q.execute(); 77 78 List<T> result = new ArrayList<T>(); 79 for (T dto : list) 80 result.add(dto); 81 logger.info("Server has fetched: " + result.size()); 82 return result; 83 } finally { 84 pm.close(); 85 } 86 } 87 88 public void add(T dto) { 89 PersistenceManager pm = getPersistenceManager(); 90 try { 91 pm.makePersistent(dto); 92 } finally { 93 pm.close(); 94 } 95 } 96 }
What do we do here:
- At line 45: we create the persistence manager with ID: "transactions-optional"
- At line 59: the addMeteo function will call the DAO (declared at line 70) in order to save the add the object
- At line 78: please note that we have to copy the objects in another list to avoid a JDO exception.
That's all for the code! Everything is now ready to put the project in the google app engine infrastructure.
Upload your google app engine
Now that everything is working: Jenkins is successfully working and you have verified the web service is working, we can upload the application on the google infrastructure:- Go in Tools->Upload App Engine Application
And that's all!!!
Warning: Do not store the GAE password in Intellij Idea and the Intellij Idea configuration file on GIT: it would mean your password (encrypted) will be stored in GIT.
15. Loading the data
In this post, I will describe how to populate the map with marker.
For this purpose, we need to know:
For this purpose, we need to know:
- the localization: add a grey marker on the map.
- the content of the plugged yoctopuce device: add a green marker on the map (localization prerequisite)
- the content of the database: red marker on the map (the plugged yoctodevice is a prerequisite)
As everything is loaded asynchronously with callbacks, we could do the following:
- get the localization
- In the callback of the location, get the content of the plugged device
- In the callback of the plugged device, get the content of the database
However, that would mean the database access is performed relatively late in the startup sequence and would cause a very slow UI.
The other approach is to load all the elements at the same time, however, depending on the responsiveness of the GPS localization, we may get the result of the yocto device before the localization. As we must know the localization when we get the yoctopuce device, we may end up with complex code that needs to check what has been already found before putting the marker on the map.
In order to simplify the flow, we will use a callback queue that will manage the callbacks priorities. The behavior will be the following:
- When a callback is called
- Check if the callback is the one with highest priority, if it is not, mark the callback as completed and do not call it
- If it is the one with the highest priority: call it
- Check the next callback (in term of priority), if is marked as finished, loop on step 3.
This behavior allows us to have the good reordering of callbacks and make sure that we will have the functions called at the beginning but with the callbacks properly ordered.
SequentialCallback
Here is the class which performs the queuing of callback. The order is specified by the sequence of the createCallback function call. For simplicity's sake, the subclasses of GenericManagedCallback are not provided here.
33 34 public class SequentialCallback { 35 36 private Logger logger = Logger.getLogger(this.getClass().getName()); 37 private List<GenericManagedCallback> callbacks; 38 39 40 public SequentialCallback() { 41 callbacks = new ArrayList<GenericManagedCallback>(); 42 } 43 44 public AsyncCallback createCallback(AsyncCallback entry) { 45 ManagedCallback callback = new ManagedCallback(entry); 46 47 callbacks.add(callback); 48 return callback; 49 } 50 51 public YoctoCallback createCallback(YoctoCallback yoctoCallback) { 52 YoctoManagedCallback callback = new YoctoManagedCallback(yoctoCallback); 53 callbacks.add(callback); 54 return callback; 55 } 56 57 private synchronized void checkTrigger() { 58 GenericManagedCallback last = null; 59 for (GenericManagedCallback callback : callbacks) { 60 if (callback.isFinished()) { 61 callback.trigger(); 62 } else { 63 last = callback; 64 break; 65 } 66 } 67 68 if (last != null) 69 while (!last.equals(callbacks.get(0))) 70 callbacks.remove(0); 71 72 } 73 74 public Runnable createCallback(Runnable runnable) { 75 RunnableCallback callback = new RunnableCallback(runnable); 76 callbacks.add(callback); 77 return callback; 78 } 79 80 81 private abstract class GenericManagedCallback { 82 private Throwable t; 83 private Object o; 84 private boolean throwableCaught; 85 private boolean objectFound; 86 87 public GenericManagedCallback() { 88 throwableCaught = false; 89 objectFound = false; 90 } 91 92 public void trigger() { 93 logger.info("Triggering current callback: " + this); 94 95 if (objectFound) success(o); 96 if (throwableCaught) failure(t); 97 } 98 99 protected abstract void success(Object o); 100 101 protected abstract void failure(Throwable t); 102 103 public boolean isFinished() { 104 return objectFound || throwableCaught; 105 } 106 107 public void onFailure(Throwable caught) { 108 throwableCaught = true; 109 t = caught; 110 checkTrigger(); 111 } 112 113 public void onSuccess(Object result) { 114 objectFound = true; 115 o = result; 116 checkTrigger(); 117 } 118 } 119 ... 205 } 206
Calling the callback
To create the callbacks, it becomes now relatively easy, here is how we get the data in the yoctopuce devices:
117 SequentialCallback callback = new SequentialCallback(); 118 119 ... 135 //noinspection unchecked 136 currentHub.refresh(callback.createCallback(new YoctoCallback<Hub>() { 137 public void onSuccess(Hub hub) { 138 logger.info("Hub with serial number: " + hub.getYocto().getSerialNumber() + " has been found"); 139 if (latLng != null) { 140 141 hub.getDto().setLatitude(latLng.getLatitude()); 142 hub.getDto().setLongitude(latLng.getLongitude()); 143 map.removeOverlay(currentMarker); 144 currentMarker = MarkerFactory.getInstance().getYoctoMarker(map, hub); 145 map.addOverlay(currentMarker); 146 147 148 //Now let's save the hub in DB 149 worldMapService.addHub(hub.getDto(), new AsyncCallback<Void>() { 150 public void onFailure(Throwable caught) { 151 logger.severe("Impossible to save hub: " + caught); 152 } 153 154 public void onSuccess(Void result) { 155 logger.fine("Hub successfully saved"); 156 } 157 }); 158 159 refreshHub(hub); 160 } 161 }
- at line 135: //noinspection unchecked: is an indication for Intellij Idea to avoid complaining about possible class cast exceptions.
- at line 139: we check if the localization has succeeded or not: we know that it has returned but we do not know if it was successful or not.
- at line 145: we add the marker on the map
- at line 149, we update the database
This concludes the introduction of the GWT & yoctopuce introduction. The next post will describe how to use Google App Engine to store the data and how to put the project in production.
14. Building the map
Template
When you work with GWT, the first thing to create is the UI... In this case, I used a very simple layout:
<html> 24 <head> 25 <meta http-equiv="content-type" content="text/html; charset=UTF-8"> 26 27 <link type="text/css" rel="stylesheet" href="WorldMap.css"> 28 29 <title>WorldMap</title> 30 31 32 <!-- Let's include google analytics--> 33 <script type="text/javascript"> 34 35 var _gaq = _gaq || []; 36 _gaq.push(['_setAccount', 'XXXX']); 37 _gaq.push(['_trackPageview']); 38 39 (function () { 40 var ga = document.createElement('script'); 41 ga.type = 'text/javascript'; 42 ga.async = true; 43 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 44 var s = document.getElementsByTagName('script')[0]; 45 s.parentNode.insertBefore(ga, s); 46 })(); 47 48 </script> 49 <script type="text/javascript" language="javascript" src="worldmap/worldmap.nocache.js"></script> 50 </head> 51 52 <body> 53 54 55 <h1>World Map</h1> 56 57 58 <div align="right">For more information: <a href="http://yocto-meteo.blogspot.com">yocto-meteo.blogspot.com</a></div> 59 60 61 <div id="worldMap"></div> 62 63 <!-- The loading image has been taken from: www.preloaders.net--> 64 <div id="loading" align="center"><img src="images/loading.gif" alt="Please wait..."></div> 65 66 <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' 67 style="position:absolute;width:0;height:0;border:0"></iframe> 68 69 </body> 70 </html>
Let me describe the interesting points in this code:
- At line 33, we add google analytics to keep track of the people coming on the page
- At line 49, we add the javascript code that will be compiled by GWT
- At line 61: we add the content that GWT will modify: it is at this location that the map will be displayed
- At line 64, we add a loading picture, that will be removed as soon as the whole content will be loaded. The picture comes from: http://www.preloaders.net
Worldmap
Now, let's see the JAVA code to display the world map:55 public class WorldMap implements EntryPoint { 56 ... 68 69 public void onModuleLoad() { 70 71 hubs = new HashMap<String, Hub>(); 72 73 //Loading page is taken from: 74 75 76 //Let's initialize the currentHub and template. 77 //The values taken are the default values 78 GWTYoctoTemplate template = new GWTYoctoTemplate("http://localhost:4444"); 79 currentHub = new Hub(new YoctoHub(template)); 80 81 //Load the Map 82 Maps.loadMapsApi("", "2", false, new Runnable() { 83 public void run() { 84 buildUi(); 85 } 86 }); 87 88 89 } 90 91 private void buildUi() { 92 93 logger.info("Build UI"); 94 95 //Note: that in order to get locations and markers: the MAP api must be loaded first. 96 createCallbacks(); 97 98 99 LatLng cartigny = LatLng.newInstance(46.1833, 6.0167); 100 101 map = new MapWidget(cartigny, 2); 102 map.setSize("100%", "100%"); 103 // Add some controls for the zoom level 104 map.addControl(new LargeMapControl()); 105 106 final DockLayoutPanel dock = new DockLayoutPanel(Style.Unit.PX); 107 108 dock.addNorth(map, 500); 109 110 RootPanel.get("worldMap").add(dock); 111 // Add the map to the HTML host page 112 113 }
The purpose of this code is to create the world map and display it on the page:
- At line 55, the class implements EntryPoint: it means that GWT knows it is the main class and that it contains a method called onModuleLoad
- At line 78, we create the YoctoTemplate and the YoctoHub to be able to contact them
- At line 82, we load the map and when the map is ready, the function buildUI is called (Here is the first contact with a callback function)
- At line 96, we create various callbacks to get: the current location, the hub values, the database content, etc.
- At line 110, we replace the content of the div world map with the map that has just been created
13. YoctoTemplate for GWT
We have seen in the Yocto-common post what was required for the YoctoTemplate. Let's now implement the functions for GWT. In this case, we will only support the asynchronous approach.
Let me describe what happens here:
YoctoTemplate
29 30 public class GWTYoctoTemplate implements YoctoTemplate { 31 32 private Logger logger = Logger.getLogger(this.getClass().getName()); 33 34 private String url; 35 36 public GWTYoctoTemplate(String url) { 37 this.url = url; 38 } 39 40 private native JavascriptYoctoMap asYoctoMap(String json) /*-{ 41 eval('var res = ' + json); 42 return res; 43 }-*/; 44 45 46 public YoctoMap query(String relativePath) { 47 throw new IllegalStateException("Not implemented yet"); 48 } 49 50 public void aSyncQuery(String relativePath, final YoctoCallback<YoctoMap> listener) { 51 52 logger.info("querying the url: " + url + relativePath); 53 String newUrl = url + relativePath; 54 55 // Send request to server and catch any errors. 56 RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, newUrl); 57 58 try { 59 builder.sendRequest(null, new RequestCallback() { 60 public void onError(Request request, Throwable exception) { 61 logger.severe("Couldn't retrieve JSON"); 62 logger.severe(exception.toString()); 63 listener.onError(exception); 64 } 65 66 public void onResponseReceived(Request request, Response response) { 67 if (200 == response.getStatusCode()) { 68 logger.info("success"); 69 String result = response.getText(); 70 JavascriptYoctoMap map = asYoctoMap(result); 71 listener.onSuccess(map); 72 } else { 73 listener.onError(new IllegalStateException("Couldn't retrieve JSON (" + response.getStatusCode() + ") (" + response.getStatusText() 74 + ")")); 75 } 76 } 77 }); 78 } catch (RequestException e) { 79 logger.severe("Couldn't retrieve JSON"); 80 listener.onError(e); 81 } 82 } 83 }
Let me describe what happens here:
- At line 46: The synchronous query will always trigger an exception. It does not make sense to support the synchronous call due to the architecture of javascript.
- At line 50, we implement the asynchronous query
- At line 56, we create the request builder for the specified URL with a method GET
- At line 59, we ask the request builder to contact the URL. We do not expect to have the result immediately but instead in a callback
- At line 60, we create the error callback function which will redirect the error to the caller directly
- At line 66, we create the callback function which will parse the result: the result will be available through the function response.getText()
- At line, we evaluate the string as a JSON object. The function is declared at line: 40
How do we convert the string into an object? That is where JSNI comes in the picture. JSNI provides a method to call directly javascript and receive the result as well. In this example, we simply say to GWT that the function is a JSNI and the code itself is evaluating the string and returns it. As long as the GWTYoctoMap class extends JavaScriptObejct, GWT will now how to cast it into the object.
Note: In this sample, you may hit an exception if you forget to set the result of the eval function in a variable: 'var res = '
JavaScriptYoctoMap
Let me describe now how to parse the JSON object in the YoctoMap object.
26 27 public class JavascriptYoctoMap extends JavaScriptObject implements YoctoMap { 28 29 30 protected JavascriptYoctoMap() { 31 } 32 33 public final native JavascriptYoctoMap getMap(String name) /*-{ 34 return this[name]; 35 }-*/; 36 37 public final native String getString(String name) /*-{ 38 return this[name]; 39 }-*/; 40 41 public final native int getInt(String name) /*-{ 42 return this[name]; 43 }-*/; 44 45 public final native JavascriptYoctoList getList(String name) /*-{ 46 return this[name]; 47 }-*/; 48 49 public final native int size() /*-{ 50 if ('__gwt_ObjectId' in this) { 51 return Object.keys(this).length - 1; 52 } 53 return Object.keys(this).length; 54 }-*/; 55 56 public final native String getKey(int index) /*-{ 57 return Object.keys(this)[index]; 58 }-*/; 59 60 61 public final native String getString(int index) /*-{ 62 return this[Object.keys(this)[index]]; 63 }-*/; 64 65 public final native int getInt(int index) /*-{ 66 return this[Object.keys(this)[index]]; 67 }-*/; 68 69 public final native JavascriptYoctoMap getMap(int index) /*-{ 70 return this[Object.keys(this)[index]]; 71 }-*/; 72 73 public final native JavascriptYoctoList getList(int index) /*-{ 74 return this[Object.keys(this)[index]]; 75 }-*/; 76 77 78 } 79
- As said before, JSNI knows how to cast objects so we do not have to bother about the cast however, there is no auto boxing, you have to say to JSNI which object you want to receive, so even if the javascript is always the same, you have to create multiple signature...
- At line 33, we can see that JSNI can cast complex objects like JavaScriptYoctoMap (as long as it extends JavaScriptObject)
- At line 49, there is a little trick. If you try to get the keys in a JavaScriptObject and you are running with the debug mode, a key is created automatically (usually the last one): "__gwt_ObjectId", in order to avoid problem in the array size, we check if the object contains the key or not.
- If a cast fails in JSNI, the error message is usually a bit cryptic and it is better to use directly a javascript debugger to understand where the error comes from.
12. GWT Configuration
When you create a GWT application, here are a few hints.
Normally a file should be created called: your_project.gwt.xml. This file will contain the description of the GWT application. In the case of the yocto-meteo project, here is the content:
Normally a file should be created called: your_project.gwt.xml. This file will contain the description of the GWT application. In the case of the yocto-meteo project, here is the content:
19 20 <module rename-to='worldmap'> 21 <!-- Inherit the core Web Toolkit stuff. --> 22 <inherits name='com.google.gwt.user.User'/> 23 24 <!-- Inherit the default GWT style sheet. You can change --> 25 <!-- the theme of your GWT application by uncommenting --> 26 <!-- any one of the following lines. --> 27 <inherits name='com.google.gwt.user.theme.standard.Standard'/> 28 <inherits name='com.google.gwt.maps.GoogleMaps'/> 29 <inherits name="com.google.gwt.logging.Logging"/> 30 <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> --> 31 <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> --> 32 33 <inherits name="yoctopuce"/> 34 35 <set-property name="gwt.logging.logLevel" value="FINE"/> 36 <set-property name="gwt.logging.enabled" value="FALSE"/> 37 <set-property name="gwt.logging.consoleHandler" value="DISABLED"/> 38 39 <set-configuration-property name="document.compatMode" value="BackCompat"/> 40 41 <!-- Other module inherits --> 42 43 <!-- Specify the app entry point class. --> 44 <entry-point class='org.yocto.sample.client.WorldMap'/> 45 </module>
- At line 20: we specify the name of the application: it is used in the HTML file including the generated javascript
- At line 22: we load the GWT core
- At line 33: we inherit the content of yocto-common (that has been created as a GWT module as well)
- At line, 28: we add two plugins: google maps in order to see the world map and the logging in order to have java.util.Logger API.
- At line 35: we specify the logging properties, by default the logging is enabled (very useful). You can decide to disable it when you go in production
- At line 44: we specify the entry point: the main class that must implement EntryPoint interface.
11. GWT introduction
Now that we have a working environment and basic API for the yoctopuce devices, let's start to work on the project.
The goal is pretty simple but will use many different pieces of GWT and Google APP engine:
The goal is pretty simple but will use many different pieces of GWT and Google APP engine:
- When I go on the web service, I want to see my geographical location and the yoctopuce devices running on my computer with their values
- I want as well to see the location of all the people who have come on the webpage and who have a yoctopuce device.
You can check the project here: http://yocto-meteo.appspot.com
And the source code is here: https://github.com/jfontignie/yocto-meteo
GWT introduction
I am not planning to explain how GWT is working, you can find very good explanations here: https://developers.google.com/web-toolkit/. Let me try to explain some key aspects of GWT:
- GWT is a java compiler which will compile your JAVA code into javascript. This implies a few "points" that will impact standard java developers:
- The whole java API is not available
- Multi-threading does not exist: callbacks have to be considered
- Debugging can work using google plugin
- JSNI (JavaScript Native Interface) is needed for some part of the development
- You can access directly the DOM.
JAVA API
As GWT compiles java into javascript, the whole java API is not available and you will have to use only a subset: URL, files and other objects do not exist in GWT. To describe an URL, you will have to use Strings instead.
Multi-threading
Multi-threading is not supported in javascript however, you can use callbacks:
- Call a particular URL
- Return directly to the caller
- When the result is available
- A callback is called where the content is provided
This has some impact in the structure of the code as a big part of the code will be directly in callbacks.
Debugging
The code generated by GWT is not really human readable and JavaScript debuggers will be almost useless with GWT. Fortunately, Google provides a very nice debugger which you can use in your Java development environment.
JSNI
In some cases, you will have to run directly some javascript functions. In this case, you can use JSNI. Here is an example:
31 32 public final native int size() /*-{ 33 return this.length; 34 }-*/;
The syntax only consists of setting the function as native and applying this particular layout: /*-{...}-*/
We will go more in details in a future post.
We will go more in details in a future post.
DOM
To change a page, it is very simple, you can call the function:
RootPanel.get("content").add(dock);
This code would for example add a new view in the DOM element called content in the current page.
Create a GWT project
To create the project, I used Intellij Idea: Create a new module from scratch and when asked for a facet, I choose "Google Web Toolkit". You can decide that GWT creates an empty project for you, it is up to you.
Inscription à :
Articles (Atom)