HTML5 is revolutionizing the web. The web application has features which are comparable to most of the desktop applications. The new features of the HTML5 make browser a powerful platform to develop next generation applications.
The APIs and document object model (DOM) are fundamental parts of the HTML5 specification. The programming language of choice in browser is JavaScript. JavaScript is a very powerful language but it is difficult to debug. Coffee Script, Google Web Toolkit, DART and other such toolkits makes it easy to write code in a language of choice and convert it into JavaScript. They work on the concept of cross compiler. The developers write in Java (in case of GWT) and the same code is later converted by GWT compiler into JavaScript.
The advantage of this approach is that an average developer can write a well modularized and maintainable code. The disadvantage of this is that every new feature in the browser which is still in experimental state need to be implemented in these toolkits. Because of this reason these toolkits are not used in designing early prototype of new features. Google Elemental is a GWT library. It uses JavaScript overlay types to generate high-performance JavaScript. Java files are regenerated out of the IDL files from WebKit. Elemental supports various HTML5 features. It includes WebSockets, WebWorkers, WebAudio, WebRTC etc. It also includes collection classes which directly map to JavaScript collections and a new JSON library which work in a browser as well as in server environment.
GWT Elemental library is blazingly fast but we find vendor prefix at present. These vendor prefix will disappear as the browser will remove the vendor prefix. At present the APIs has webkit as vendor prefix and hence the cutting edge features work only in Chrome and in future we see better support for other modern browsers.
GWT Elemental is new library and there is absolutely zero documentation on the library. In this article we use some HTML5 features like WebWorker, WebSockets andFileSystem APIfrom gwt-elemental library. The samples shown here are very simple but they give an idea to create large applications. Gwt-elemental provides elemental.Window
class to create objects of different classes. This Window object is different from com.google.gwt.user.client.Window
To start using GWT Elemental library add gwt-elemental in the classpath of the GWT Project. By default at present when we create the GWT project gwt-developer and gwt-client are only libraries which are set by GWT_HOME variable. Inherit gwt-elemental in the project module file (ProjectName.gwt.xml) in the project. Below images show some of the screenshots from eclipse.
We will look into some of the features of the web browser here using gwt-elemental library.
1) Web Worker
A Web Worker is JavaScript thread which can run on client side. This is used to do heavy computation task without blocking UI thread. The DOM is single threaded and here multiple scripts cannot run at the same time. If a script takes long time to execute in the UI thread, then it will be blocked and the page becomes unresponsive. A web worker can’t manipulate DOM. Using Web Workers, background scripts can be spawned in the web application.
A new worker object is created in the main page and the code for the worker is written in a separate file.
//worker.js
var n = 1; search: while (true) { n += 1; for (var i = 2; i <= Math.sqrt(n); i += 1) if (n % i == 0) continue search; // found a prime! postMessage(n); }
Communication between a worker and the main page takes place using an event model and the postMessage()
method. This method is used to send data to the worker as well as for passing data back to the main thread.
Example: In the below code, after clicking on start button, message “Hello worker” is send to the worker. Clicking on stopButton terminates the worker.
privatestatic Window window = elemental.client.Browser.getWindow(); worker = window.newWorker("worker.js"); final Button startButton = new Button("Start"); startButton.addClickHandler(new ClickHandler() { @Override publicvoid onClick(ClickEvent event) { worker.postMessage(“Hello worker”); } }); final Button stopButton = new Button("Stop Worker"); stopButton.addClickHandler(new ClickHandler() { @Override publicvoid onClick(ClickEvent event) { worker.terminate(); }});
Event Handlers onMessage
and onError
are defined which will be executed when main thread receives any error or any message from the worker.
worker.setOnerror(new EventListener() { @Override publicvoid handleEvent(elemental.events.Event evt) { window.alert("Error message"+evt.toString()); } }); worker.setOnmessage(new EventListener() { @Override publicvoid handleEvent(elemental.events.Event evt) { if(evt instanceof MessageEvent){ MessageEvent event = (MessageEvent)evt; Object obj= event.getData(); updateLabel.setText(obj.toString()+"Data"); }else{ updateLabel.setText("Message not came"); } } });
2) Web Socket
Long polling and the other techniques, where the client opens an HTTP connection to the serverand the connection is kept open until response is sent, work well; but they carry the overhead of HTTP. The WebSocket protocol uses the HTTP upgrade system to upgrade an HTTP connection to a WebSocket connection.
Web sockets allow us to establish connection between the client and the server and both client and server can start sending data at any time. Gwt-web-sockets provide a simple GWT wrapper around the browsers native Web Socket implementation.
Create a Web Socket and Set OpenHandler, CloseHandler and MessageHandler for processing Web Socket events.
WebSocket socket = window.newWebSocket(url); //url of server is provided
socket.setOnopen(new EventListener() { @Override publicvoid handleEvent(Event evt) { // steps which are executed when socket connection is opened } }); socket.setOnclose(new EventListener() { @Override publicvoid handleEvent(Event evt) { // steps which are executed when socket connection is closed } }); socket.setOnmessage(new EventListener() { @Override publicvoid handleEvent(Event evt) { // steps which are executed when a message is received from the server } });
Use socket.send(String data)
to send messages to the server.
//Server side code to establish a connection and handle the client request
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { returnnewServerSocket(this); }
import org.eclipse.jetty.websocket.WebSocket.Connection; publicclass ServerSocket implements WebSocket.OnTextMessage { private Connection _connection; @Override publicvoid onClose(int closeCode, String message) { // steps to be executed when socket is closed } publicvoid sendMessage(String data) throws IOException { //to send message to the client _connection.sendMessage(data); } @Override publicvoid onMessage(String data) { // steps to be executed on receiving data from clients } publicboolean isOpen() { // returns whether the connection is open or not return_connection.isOpen(); } @Override publicvoid onOpen(Connection connection) { _connection = connection; // steps to be executed when the connection is opened }
3) File System API
Web Applications have become more complex and they demand many features which were available on desktop. For example storing and retrieving file is one such example. This can be used in Browser based IDE and other similar applications. We can do similar thing using IndexDB but application logic becomes more complex to store and retrieve the data to emulate file system. The new File System API solves the problem. These API helps you to create file, read, write, manipulate and navigate the file in sandboxed section of user’s local file system. Files are represented by FileEntry interface. Due to Same Origin Policy, one web app cannot interact with the storage of other web app. The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin.
There are two types of storage in case of File System API:
- Persistent Storage
- Temporary Storage
Data stored in temporary storage can be removed at the browser’s discretion whereas persistent storage cannot be cleared unless explicitly authorized by the user or the app. We must obtain permission from user to store persistent data. To request storage, window.webkitStorageInfo API is used.
Mozilla supports only FileReader whereas Chrome supports both FileReader and FileWriter.
Following code snipped depict the usage of gwt-elemental to use File System API. Using window.webkitRequestFileSystem()
, a web application can request access for sandboxed section of user’s file system.
Window window = Browser.getWindow(); window.webkitRequestFileSystem(type, size, successCallback, optionalerrorCallback)
Here, type specifies whether the file storage should be persistent or not.Type
can be window.TEMPORARY
or window.PERSISTENT
. Size specifies the number of bytes required for storage. successCallback
and optionalerrorCallback
are the callback methods executed on request success or error respectively.
Creating a file:
Files are represented by the FileEntry interface. File System’s getFile()
method of DirectoryEntry interface is used to create a file. After requesting the file system, successCallback
receives DOMFileSystem object. FileSystem object is obtained as follows.
publicboolean onFileSystemCallback(DOMFileSystem inFileSystem) { fileSystem = inFileSystem;}
In success callback, we can call getFile()
method with the name of the file to create.
DirectoryEntry directoryEntry = fileSystem.getRoot(); JsonObject object = JsJsonObject.create(); object.put("create", true); object.put("exclusive”, true); directoryEntry.getFile(FileName, object, successCallBack,errorCallback)
Here, create = true is used to create the file if it doesn’t exist and it throws an error if it does (exclusive= true). If create = false, the file is simply fetched and returned. For appending and removing a file create=false is provided in the JsonObject.A
. JsonObject is an unordered collection of name/value pairs. Put()
method is used for adding or replacing values by name.
The above snapshot shows the creation of demo.txt with the given file content. An extension called HTML5 FileSystem Explorer can be downloaded from Here. Theextension help developers to browse into the contents of their local Web filesystem created by FileSystem API.
Reading a file:
FileReader API is used to read a file. In the code listed below, a file is read and the contents are displayed in a TextArea. errorCallback
is used to handle errors.
FileEntry fileEntry = (FileEntry)entry; FileCallback callback = new FileCallback() { publicboolean onFileCallback(File file) { final FileReader reader = window.newFileReader(); reader.setOnloadend(new EventListener() { publicvoid handleEvent(Event evt) { txtArea.setText(reader.getResult().toString()); }}); reader.readAsText(file); returntrue; } }; fileEntry.file(callback, errorCallback);
Writing a file:
FileEntry’s createWriter()
method is called to obtain a FileWriter object. A blob object is created and passed to FileWriter.write()
method. Here, the text inside the TextArea is passed to createBlob()
method which will create a blob object.
FileWriterCallback callBack = new FileWriterCallback() { publicboolean onFileWriterCallback(FileWriter fileWriter){ fileWriter.write(createBlob(writeTextArea.getValue())); returntrue;} }; fileEntry.createWriter(callBack, errorCallback);
Appending to a file:
For appending data to a file below code is written. And then the blob object is passed to the write method. Seek is used to get the location of the end of file.
fileWriter.seek(fileWriter.length); // start position for writing is set at EOF
Deleting a file:
For deleting a file following line of code is written.
fileEntry.remove(successCallback, errorCallback);
Creating a directory:
Directories are represented by the DirectoryEntry interface. getDirectory()
method of DirectoryEntry is used to read or create directories.
DirectoryEntry directoryEntry = fileSystem.getRoot(); JsonObject object = JsJsonObject.create(); object.put("create", true); directoryEntry.getDirectory(DirName, object, entryCallback errorCallback);
Deleting a directory:
DirectoryEntry.remove()
method is used to remove a directory. It will throw an error if the directory we are trying to delete is not empty.
DirectoryEntry dirEntry = (DirectoryEntry) entry; dirEntry.remove(new VoidCallback() { publicvoid onVoidCallback() { window.alert("Directory removed"); } }, errorCallback);
Listing files and directories:
For listing all the files and directories present in the root, a DirectoryReader is created and readEntries()
method is called.
window.webkitRequestFileSystem(Window.PERSISTENT, 1024 * 1024, new FileSystemCallback() { publicboolean onFileSystemCallback(DOMFileSystem fileSystem) { DirectoryEntry directoryEntry = fileSystem.getRoot(); directoryEntry.createReader().readEntries(new EntriesCallback() { publicboolean onEntriesCallback(EntryArray entries) { for (int i = 0; i < entries.getLength(); i++) { Entry entry = entries.item(i); listFiles.addItem(entry.getName()); } returnfalse; } }, errorCallback); returnfalse; } });
These were some of the operations performed using GWT Elemental.
Conclusions:
We show some of the features of HTML5 usage using gwt-elemental API. Though it is a new library it has potential to build large scale application in enterprise. In next article we utilize this library to build few of the command of GIT (Distributed Version Control System) using File System API.
Visit InstantShift
InstantShift