Get Started 3: the HelloWorld App

Converted document

The HelloWorld App in webAppOS

The app will demonstrate how to write a web app using some server-side Java code and some client-side JavaScript code. The goal is to store a message in the web memory at the server side and then display it at the client side.
Refer to the dist/apps/HelloWorld.webapp directory in the sources tree at GitHub for full sources of this example.

Prerequisites

Please, install Java Development Kit and webAppOS for developers. We use dist to denote a directory, where webAppOS has been installed.

Creating the App Configuration

Since apps are located in the apps directory, we will put our app into the dist/apps/HelloWorld.webapp subdirectory. We start by filling the webapp.properties file inside that directory.
The main app icon should be placed into dist/apps/HelloWorld.webapp/web-root/icon.svg (or .png, or .jpg). It will be used by the Desktop app and by the client-side webappos.desktop.show_launcher function that can be invoked from any webAppOS app to show the list of the available apps.

Creating Web Memory Data Model

We will use a simple data model in this app. We will introduce a class named HelloWorld and a string attribute named message. See dist/apps/HelloWorld.webapp/HelloWorld.ecore or dist/apps/HelloWorld.webapp/HelloWorld.mmd for the formal specification of the model in the de facto EMF/ECore modeling standard or a simple .mmd format.
To be able to access web memory objects as if they were native Java objects, we invoke the mmd2java (for .mmd metamodels) or ecore2java (for .ecore metamodels) tool, bundled with webAppOS, to generate Java classes from the model (the gen_java_classes.bat/.sh script invokes the mmd2java tool). However, if you prefer low-level access to the web memory, you can skip this step. The command syntax for mmd2java is
mmd2java [metamodel].mmd [directory/for/generated/src] [java.package.name]
A command line example (to be launched from the HelloWorld.webapp directory):
../../bin/mmd2java HelloWorld.mmd src org.webappos.apps.helloworld.mm
The command syntax for ecore2java is:
ecore2java [metamodel].ecore [directory/for/generated/src]
A command line example (to be launched from the HelloWorld.webapp directory):
../../bin/ecore2java HelloWorld.ecore src
For accessing web memory objects from JavaScript, the corresponding JavaScript wrappers will be created automatically by the client-side script webappos.js.

Defining Web Calls Actions

We create a file dist/apps/HelloWorld.webapp/HelloWorld.webcalls and define 4 actions (refer to the .webcalls file format), which will be described below.

The HelloWorldMain action

webmemcall\ HelloWorldMain=staticjava:org.webappos.apps.helloworld.HelloWorld#initial
The first line in the HelloWorld.webcalls file describes the HelloWorldMain action, which is being referenced in the main property in webapp.properties. Thus, this action will be called each time a project is created/opened. Let’s create a Java implementation for it. The built-it staticjava adapter (the adapter name is between ‘‘=’’ and ’’:’’) is able to invoke static Java functions. In our case, we instruct it to search for the initial function in the class org.webappos.apps.helloworld.HelloWorld. The adapter will need to pass some arguments to this function. Since we specified the webmemcall calling conventions (in the beginning of the line), the function should look like:
public static void initial(IWebMemory webmem, String project_id, long r) {
...
}
Here:
Since we do not want to use the low level API (IWebMemory), we initialize our generated Java classes via the elevate method, which returns a Java factory for accessing and creating web memory objects. The factory class name corresponds to the metamodel name. The factory instance has to be passed to certain methods of generated classes.
import org.webappos.apps.helloworld.mm.HelloWorldMetamodelFactory; // generated
import org.webappos.webmem.IWebMemory; // the interface for accessing web memory
...
​
public class HelloWorld { 
	public static void initial(IWebMemory webmem, String project_id, long r) 	{		 	
		HelloWorldMetamodelFactory factory = webmem.elevate(HelloWorldMetamodelFactory.class); 
		...***...
	}
}
Then, in the ‘‘...***...’’ part, we write our code. The metamodel (just the HelloWorld class and the message attribute, in our case) will be present in the web memory. We check, whether this class has an instance, and initialize the message string accordingly:
			org.webappos.apps.helloworld.mm.HelloWorld objectWithMessage
				= org.webappos.apps.helloworld.mm.HelloWorld.firstObject(factory);
​
			if (objectWithMessage==null) {
				objectWithMessage = factory.createHelloWorld();
				objectWithMessage.setMessage("Hello for the first time!");
			}
			else
				objectWithMessage.setMessage("Hello again!"); 
Then we want to pass this message to some client-side code. To demonstrate both fundamental ways of passing the argument, we will invoke two web calls: the first one passing the argument as a JSON string and the second one passing the argument as a reference to a web memory object (wrapped by Java objectWithMessage in our case). At the server side, web calls can be enqueued using the webCaller field of the org.webappos.server.API class. Before enqueuing, the corresponding web call seed filled with web call-specific data has to be prepared.
			WebCallSeed seed = new WebCallSeed();
			seed.actionName = "ShowMessageFromJSON";
			seed.project_id = project_id;
			seed.jsonArgument = "{\"message\":\""+objectWithMessage.getMessage()+"\"}";
			seed.callingConventions = IWebCaller.CallingConventions.JSONCALL;
			API.webCaller.enqueue(seed);
​
			WebCallSeed seed2 = new WebCallSeed();
			seed2.actionName = "ShowMessageFromWebMemory";
			seed2.project_id = project_id;
			seed2.webmemArgument = objectWithMessage.getRAAPIReference();
			seed2.callingConventions = IWebCaller.CallingConventions.WEBMEMCALL;
			API.webCaller.enqueue(seed2);
Now, let’s implement client-side functions ShowMessageFromJSON and ShowMessageFromWebMemory, which will be called on these two web calls.

The ShowMessageFromJSON and ShowMessageFromWebMemory actions

First, we delcare ShowMessageFromJSON and ShowMessageFromWebMemory in dist/apps/HelloWorld.webapp/HelloWorld.webcalls as functions called via the ‘‘clientjs’’ (client-side JavaScript) adapter.
jsoncall\ ShowMessageFromJSON=clientjs:helloFromJSON
webmemcall\ ShowMessageFromWebMemory=clientjs:helloFromWebMemory
Then, in the app web-root directory (dist/apps/HelloWorld.webapp/web-root), we create the index.html file having a script tag implementing these two functions:
<script>
async function helloFromJSON(json) {
	alert(json.message+" / web memory reference="+json.reference+" (must be undefined)");
	let json2 = await webappos.webcall("AddWorldToHello", json.message);
	window.webappos.desktop.show_dialog("Adding world", json2.result);
	return {}; // jsoncall calling conventions require to return a JSON
}
​
function helloFromWebMemory(obj) {
	alert(obj.getMessage()+" / web memory reference="+obj.reference+" (must be some integer)");
}
...
</script> 
Notice that the helloFromJSON function makes a web call (via webappos.webcall) back to the server and invokes the AddWorldToHello action. The returned JSON will contain the result attribute, which we will display not as an alert, but using webappos.js Desktop API function show_dialog.
Finally, to connect to the web memory from the client side, we add the webappos.js script to index.html
<script src="webappos.js"></script>
and invoke the following JavaScript code:
webappos.request_scopes("webappos_scopes", "project_id").then(
	()=> alert("Web memory initialized.")
);
The request_scopes function will display the user authentication (if the user has not been authenticated yet). It will also take care of asking the user where they want to create a new project or browse for an existing one (in case the project_id was not specified in the query string).

The AddWorldToHello action

Finally, we implement the server-side action AddWorldToHello (used as a callback in the client-side helloFromJSON function). In HelloWorld.webcalls, we define AddWorldToHello as a static Java function addWorld:
jsoncall\ AddWorldToHello=staticjava:org.webappos.apps.helloworld.HelloWorld#addWorld 
The implementation is simple:
public static String addWorld(String s)
{
	if (s==null)
		return "{\"error\":\"Null string passed.\"}";
	else
		return "{\"result\":\""+s.replace("Hello", "Hello, world,")+"\"}";
}
When the staticjava adapter makes a web call according to the jsoncall calling conventions, it passes the argument as a string (which can be any string, stringified JSON not required), and expects the result to be a stringified JSON.

← Previous | Next →