GWT

StrataCode Web Framework: SCHTML, JavaScript, Sync

Why another web framework? Here are the advantages, and details on the implementation of the key features: java to javascript, tag objects.

Let's get started:

Download and install StrataCode. Put the 'bin' directory into your path or create an alias for bin/scc. Go to the layers folder and run:

scc example/simpleSync/main

This runs a simple hello world application that uses both client and server.

Simple Example

Here's a simple hello world template:

file: test/js/helloWorld/helloWorld.schtml
<html>
  <body>
    <div class="appFrame">
       <%! String yourName = ""; %>
       <div>
         <label>Name:</label>
         <input type="text" value=":=: yourName" placeholder="Enter a name here">
         <hr>
         <div><h1>Hello <%= yourName %>!</h1></div>
       </div>
    </div>
  </body>
</html>

The <%! directive works like in JSP - you are adding a type declaration, in this case a field named yourName that's visible using the same scoping rules as Java when you consider each tag as an object where inner tags are inner objects. In this case, it's visible to the other children of the 'div class="appFrame"' tag.

The input tag's value attribute uses a data binding expression to bind the value to yourName. Attributes can refer to Java expressions with or without data binding by prefixing the attribute value with the "=" operator, or one of the data binding operators: ":=", ":=:", or "=:".

When you use just the = operator, it just initializes the attribute when the tag's object is first created.

The <%= directive also works like JSP. Evaluate the expression and output the result.

UnitConverter Example

Here's the unitConverter's user interface implemented in HTML:

file: example/unitConverter/jsui/UnitConverter.schtml
<%@ @URL(pattern="/uc") %>
<html>
<head>
	<title>UnitConverter on StrataCode</title>
	<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>

   <div class="appFrame">
      <form id="unitConverterForm">
         <select id="converterChoice" 
               optionDataSource=":= converters" 
               selectedIndex=":=: currentConverterIndex"/>
         <br>
         <span id="unit1Label" class="unitLabel">
              <%= currentConverter.unit1 %>
          </span>
         <input id="unit1Field" class="unitField" autoComplete="off"
                value=":=: numberConverter.numberToString(currentConverter.value1)"/>
         <br>
         <span id="unit2Label" class="unitLabel">
             <%= currentConverter.unit2 %>
          </span>
         <input id="unit2Field" class="unitField" autoComplete="off"
                value=":=: numberConverter.numberToString(currentConverter.value2)"
           />
      </form>

      <div id="feedback"><%= numberConverter.error %></div>
   </div>

</body>
</html>

Notice a few things. It uses the same base layers as the other UIs so the domain model is kept cleanly in code. All we're doing here is binding the user interface to existing model elements.

The template itself is normal HTML that can be previewed statically in the browser or edited with an HTML editor. Expressions will show up as formulae you can edit in the external tool.

This same template can either run on the client, server or using a mix based on how the framework chooses to run the system on a tag-by-tag basis. Data binding keeps everything in sync across the client/server boundary. Script code written in StrataCode is converted to Javascript as needed. Explicit calls to change attributes or content can be made to implement more complex logic that data binding code can't handle.

Run on the Client, Server, or Both

Some templates have to run on the server. For those, put them in a layer that only extends jetty.schtml.

Other templates may only run on the client. Put those in a layer which extends js.schtml. When your layer extends a layer which has an explicit dependency on one runtime, it inherits that dependency by default.

For templates that you want to run in any configuration, have their layer extend just html.schtml. Before you run your application, you will need to add either js.schtml or jetty.schtml for it to do anything, but you can do that from the command line or add that dependency in an upstream layer.

HTML and Javascript Applications

When you use just js.schtml, you'll build a website from only html and javascript files. You can open those files in your browser or publish them with another web server. In this configuration, StrataCode generates .java files from the schtml and sc files in your project, then converts those files to .js which is loaded by the browser.

After you build your project, the layer's build directory contains a directory called web - the top-level directory for your site. Your browser opens to the index.html file unless you run StrataCode with the -nw (no window) option.

StrataCode generates a default index.html file which contains links to the top-level URLs defined in your project. This includes all of the schtml files that have an html tag in them or types you mark with @URL. As soon as you add an index.html or index.schtml file to your project, it overrides the default one. Like any other path in the source directory, a subsequent layer can simply override it by adding one to replace it.

Some schtml files are typically used as HTML fragments. Those pages do not start with the html tag itself. If you add an @URL annotation to any Servlet or schtml page, you can access it from a pre-defined URL instead of using the paths of the pages directly.

Client/Server Applications

When you include the jetty.schtml layer, StrataCode generates a class called PageInit to initializes all of the top level URLs and registers them with the PageDispatcher servlet. This will include those types marked with @URL or which have an <html> tag as their root but this is configurable. This servlet maps the page requests from URLs to code which uses your server-side template object to generate an HTML response.

URLs and Template Pages

Any schtml files you put your layer directory are put into the corresponding directory in the generated web root. This allows you to keep code and templates together in the normal Java package hierarchy, so you can override one by another. It also gives you a simple default URL naming scheme which you can use to replace template pages, even when the package name does not match.

You can also control what URLs map to which pages via the @URL annotation. This annotation lets you associate a pattern string which matches components of the URL, such as path-name entries or query parameter, to properties values. When a query pattern matches an incoming request for a URL, that page is used to retrieve the contents. But first the pattern extracts the values from the URL uses them to set properties on the page object. TODO: provide an example of @URL(pattern="...") here

Synchronization

When your layers extend both jetty.schtml and js.schtml, you can also enable StrataCode synchronization.

You can set the defaultSyncMode to SyncMode.Automatic in your layer definition file for any layers which are run in more than one runtime. Types and properties defined in that layer are all registered with the synchronization system using the same get/set conversion and data binding logic. Rather than writing marshalling and unmarshalling code using explicit client/server APIs, most of your application can be moved seamlessly between the client and server, or both based on how you want the data to flow.

You can augment the automatic synchronization by annotation driven synchronization. Here you explicitly mark properties and types with @Sync annotation, using attributes to control how and when the object is fetched and updated.

Using StrataCode synchronization provides you with high-end application behavior from your domain model in a pure state. You get immediate page loads and can refresh the page at any point without losing any browser state. You have the efficiency of sticky-session load balancing, and the robustness of stateless operation - where all server-side state can be recovered from the client when the server process restarts. All of this happens transparently when you properly partition your code into layers and turn on automatic synchronization.

Here's how this complex behavior works. The server renders an initial HTML page is delivered to the browser based on the URL request. After the initial HTML is displayed to the user, the client starts downloading the Javascript code to incrementally update the page and perform the synchronization. This includes the JS code from your schtml templates, your domain model layers included on the client, and the synchronization hooks injected into your code for firing change events as well as the JS code for implementing the SC web framework. It then downloads a Javascript code snippet to initialize the state of any synchronized object. This is called a "sync layer", representing the changes for the first sync from the server to the client.

As changes are made on the client those changes are recorded in a runtime layer of data called a "sync layer". When necessary, the client sends its current sync layer to the server. This sync layer works like a JSON file though it can supports any kind of change you might make to the server. It can include method calls, new instance creation, etc.

The server applies that layer of changes and records any subsequent changes made in response. Commonly you will change properties which trigger data binding expressions on the server. These rules produce new property changes, or may call methods which create an entire object graph. The changes made on the server are recorded just like on the client and then sent back as the response to this request. This natural batching of changes gives you nice seamless client/server communication with good performance characteristics based on the batching that occurs naturally.

The client applies the response which typically updates the user interface displays or triggers additional UI display logic.

There is a lot of moving parts but they are all carefully designed to be flexible, efficient, and maintainable. For example, it's easy to trace the generated code injected into the Java for synchronization to be sure it matches what you want. Also, The formats exchanged are easy to read and debug. See debugging for details.

Overall we believe the sync framework provides a clean way to implement declarative, client/server from a single code base.

You can of course mix in explicit RPC or use data binding to call remote methods automatically.

Data Synchronization - Client/Server Communication

The @sc.obj.Sync annotation is used to mark classes that should be synchronized. For layers, types, or properties which set the @Sync(syncMode=SyncMode.Automatic), any property which exists in the same type in both the client and server runtime is synchronized automatically.

For example, the shared model code runs on both client and server:

file: example/simpleSync/model/HelloWorld.sc
object HelloWorld {
   String message = "hello";
   String reply = "";

   message =: System.out.println("--- Message: " + message);
   reply =: System.out.println("--- Reply: " + reply);
}

When reply and message properties change, both the client and server will print messages to System.out.

The user interface template code also runs on both client and server:

file: example/simpleSync/ui/HelloWorld.schtml
<!-- A simple HTML page template which binds the input tag to the message property and displays 
     the reply property as the body of a div tag. -->
<html>
<body>
   <form onSubmit="return false;">
      <input type="text" value=":=: message" size="45">
      <div><%= reply %></div>
   </form>
</body>
</html>
The server code runs only on the server. This might be a database query:
file: example/simpleSync/server/HelloWorld.sc
// This runs on the server - it could be a database query or secure logic you do not want
// to expose on the client. 
HelloWorld {
   reply := "Server says: " + message;
}

These layers naturally split themselves out when you consider that your database methods tend to be exposed only on server specific objects. The logic that depends on those methods then belongs on the server. It may set properties which are shared which are synchronized to the client.

When you can use data binding and change events to relate client-server data exchanges, you can implement fully declarative applications.

For more information see details on the sync framework.

Mixing HTML and Java

For a more complex sample here is the domain model for a TodoList application. Simplified Java in the base layer: example/todo/model:

file: example/todo/model/TodoList.sc
class TodoList {
   @Component
   class TodoItem {
      String text;
      boolean complete;

      TodoItem(String t, boolean c) {
          text = t;
          complete = c;
      }
   }
   ArrayList<TodoItem> todos; /* = {
      new TodoItem("Run layerCake todo sample", true),
      new TodoItem("Check me and see it stay in sync", false),
      new TodoItem("Add a new entry and press 'remove completed'", false),
   } */;

   String todoText = "";

   void addTodoEntry() {
      todos.add(new TodoItem(todoText, false));
      todoText = "";
   }

   int getRemaining(List<TodoItem> todoList) {
      int count = 0;
      if (todoList == null) {
         System.out.println("*** no list");
         return 0;
      }
      for (TodoItem todo: todoList) {
         if (!todo.complete)
            count++;
      }
      return count;
   }

   int getSize(List<TodoItem> list) {
      return list == null ? 0 : list.size();
   }

   void removeComplete() {
      ArrayList<TodoItem> newTodos = new ArrayList<TodoItem>();
      for (TodoItem todo: todos) {
         if (!todo.complete) 
            newTodos.add(todo);
      }
      todos = newTodos;
   }
}
In the example.todo.jsui layer, which extends the model layer, the template page is merged with the model because their file names are the same. The sub layer can access or override the base layer just as if itwere extending it.
file: example/todo/jsui/TodoList.schtml
<!-- TodoList.schtml (by default) modifies the TodoList class in the previous model layer.
     It sets needsRefresh so we refresh the bindings on this page for updating the getRemaining
     method. -->
<html needsRefresh="true">
  <head>
      <link rel="stylesheet" type="text/css" href="todoStyle.css" />
  </head>
  <body>
    <div class="appFrame">
       <h2>Todo List</h2>
       <div id="todoControl">
         <!-- Not just a script!  These values are updated automatically -->
         <span><%= getRemaining(todos) %> of <%= getSize(todos) %> to do</span>
         <!-- clickEvent changes when there's a click.  When that happens the binding calls: 'removeComplete()' -->
         [ <a href="#" clickEvent="=: removeComplete()">remove completed</a> ]
         <ul>
           <!-- Repeat the li tag once for each element in the todos list.  
                As the list changes, tags are added/removed as needed -->
           <li repeat=":= todos" repeatVarName="todo">
             <!-- Set checked to the value of todo.complete and vice-versa -->
             <input type="checkbox" checked=":=: todo.complete">
             <!-- Add a class to this span tag when it is done -->
             <span class='<%= "complete-" + todo.complete %>'><%= todo.text%></span>
           </li>
         </ul>
         <form submitEvent="=: addTodoEntry()">
           <input type="text" value=":=: todoText" size="45" placeholder="enter todo entry here">
           <input type="submit" value="Add" disabled=':= TextUtil.length(todoText) == 0'>
         </form>
       </div>
     </div>
  </body>
</html>

One-Step Build/Run

The scc command does it all. Even with client/server applications, it will do all code generation and compilation necessary, start processes, open browser windows, and implement restart, so you have full life-cycle control over the application. This will make it easy to deliver SC as both a SaaS and OnPremise solution. Let you support client-only, server-only, and client/server mixed solutions.

The layer architecture will let you start or attach to a server, start any necessary processes and run any necessary main methods based on the current layer set. Any SC process can change any object or model and sync with the other federated processes, allowing them to update both runtime views and development time views "in sync".

Automatic Refresh of Declarative Templates

StrataCode templates generate code in two different ways depending on the configuration and the content of your template. When your tags only use declarative constructs, i.e. properties and data binding expressions they support incremental evaluation. As properties fire, only the affected chunks of text are updated - .e.g an attribute is replaced, or part of an expression evaluated. These template fragments are converted into data binding expressions similar to:

chunk1 := variable1 + "constant1" + variable2 + "constant2";
chunk1 =: invalidateStartTag(); // or invalidateBody if the expression is in the body

As the data values change, only the affected portions of the template are rendered. This provides super efficient caching on the server and incremental refresh for interactivity on the client.

When you use any non-declarative constructs in your tag, that tag cannot use this mode for its content. It falls back to generating an output method which appends to a StringBuilder using the values in the expression. When you use this mode, these regions of template must be refreshed manually using the tag API.

To make your templates declarative, here are a few tips:

SCSS

StrataCode supports the use of the scss extension to generate CSS files automatically using the template language. These files produce css files by evaluating the template. Just like all templates, they can be processed at build time or generated dynamically when you request their URL. Like SCHTML, SCSS templates that are declarative are updated automatically when properties change. This lets you use SCSS files to specify rules that evaluate at runtime.

You may not want to use SCSS files at all or only to define static css files at compile time. CSS files are by their role largely unchanging so maybe one should keep it that way. You can always put dynamic styles into SCHTML files with the rest of the dynamic logic, or put static CSS in one file and dynamic rules in another SCSS file, or the same file in another layer.

CSS by itself does not offer you all of the language features you need to prevent lots of copying. When you copy, you make it that much harder to maintain. Many other formats layer on top of CSS to solve this problem and those could all easily be integrated as framework layers in StrataCode.

The advantages of SCSS are:

Here's an example:

      <%!
         String borderColor = "#777";
         // With the SC template language, you can break back into a template expression for any string using >%
         // The template language is really Java where strings are turned inside-out
         String dropShadow = %>
            border-style: solid;
            border-width: 1px;
            border-color: <%= borderColor %>;
            box-shadow: 5px 5px 2px #aaaaaa;
         <%;
      %>

      #navMenu li ul {
         &;lt;%= dropShadow %>
      }

      #navMenu li ul ul {
         <%= dropShadow %>
      }

      .toggleButtonSelected {
         border-color: <%= borderColorSelected %>; 
      }

Images, Static HTML Etc.

You put images, .html files and other static resource files in your layer directory as the top-level and they are copied into the web foler automatically. Unlike source files, the layer's package is not prepended onto the generated file.

This lets you replace any file by just defining a file with the same name in the same path in a subsequent layer.

An html file can replace an schtml file and vice versa.

Reusable Tags

It's very common to have tags and attributes in HTML that you need to reuse, or re-parameterize in some way. Turns out that this is a very tricky problem and important to get right. Java's inheritance got us most of the way there, but StrataCode adds the last mile. So SCHTML is a thin-skin on top of StrataCode's code-generation, which in turn is converted to Java in an intuitive way.

Each tag that needs an object becomes a StrataCode object (which in turn may become a Java class if one is needed). If the tag has an id attribute, you have a predictable name for that object and can use it in the extends attribute from another tag. Keep in mind Java's inner class name visibility rules. To use a tag name, it must be in scope. In other words, the name must be found in a direct search of the id's of the children of the current parent or any id in any parent tag above in the hierarchy.

With SCHTML, you can create tag macros which take typed parameters, either in the same file or in a separate file. When you define a macro in the same file, use the abstract="true" attribute so that tag is not inserted into the output of the template. In the generated code, an abstract="true" tag generates a Java class, not an object that's inserted into the output.

When you have an SCHTML file with a single tag in it, that tag's id automatically becomes the name of the file. So if you define a file called "MyMacro.schtml" with a single div tag with no id, you can use extends="MyMacro". When one tag extends another, by default, both the attributes and body of the extended tag are rendered. Any attributes set in the tag replace those in the extended tag. The extended variables are available via "super.x" if you want to do dynamic calculations on them. The extended tag's body is also initially the body used for the output. Any new tags you provide are merged into the body of the extended tag. Tags with the same id inside of the body are themselves merged and so on.

TODO: currently any tags without any id which need objects for some other reason are merged. This causes a usability problem but is necessary for html, body, head to work correctly. It should be corrected by making this switchable on the tag name. div, p etc. should not be merged but appended to by default. There's a notion of that tag ids that are globally unique in the page and we should do the merge for those tags and append for these anonymous tags. In the meantime, use ids for the sub-tags of any tag macros for fear they match accidentally and cause weird results.

The tags you extend should have the same tag name. So a 'div' tag should always extend another div tag. The role the tag plays seems to be indicated by a preferred tag in all cases. The structure imposed here provides good error messages when intent and code do not line up.

Inside of your tag, you can define properties using the <%! operator. You can set these properties using attributes from the extending tag. The transformation process converts each attribute setting into a property assignment, so they override any previous values those properties might have had. This also means strong-type checking is performed so good errors when you mess up, and all of the other benefits of strong-type checking.

Be aware that you do need to use: attributeName="= expression" unless the attribute is a String value. The 'expression' part is Java expression and so converted and type-checked properly. When you leave that out, it's a string and only works with String properties (TODO: maybe we should support some basic type conversion here?) If you think the expression will change at runtime, use attributeName=":= value" so the value is updated on the fly. SC takes care of adding the necessary getX and setX methods along with the sendEvent call to trigger the update.

Here's an example that demonstrates reusable tag macros:

file: test/js/simplePerson/PersonPage.schtml
<html>
<body>
<%! 
// Template declarations - add an inner class and two fields
class Person {
   String name;
   int credits;
   Person(String n, int c) {
      name = n;
      credits = c;
   }
}

Person buyer = new Person("Franklin", 250), seller = new Person("Mario", 5800); 
%>

<h2>People</h2>
Here are the people:

<!-- Define PersonTemplate an 'abstract' template meaning it is not drawn itself.  
     It has a "data" field of type Person we can populate through attributes. -->
<div abstract="true" id="PersonTemplate">
  <%! Person data; %>
  <h3><%= data.name %></h3>
  <p id="creditsParagraph">Credits: <span><%= data.credits %></span></p>
</div>

<!-- Render the PersonTemplate tags once binding data to buyer and then again for seller -->
<div extends="PersonTemplate" data="= buyer"/>
<div extends="PersonTemplate" data="= seller"/>
 
</body>
</html>

Client/Server Java Tag API

StrataCode provides a thin wrapper on top of the core Javascript document object model api (DOM) to let you manipulate tags in a way that works on client and server. This API provides bindable properties you can use in your templates to change the HTML, or react to events such as click, mouse move, change, or resize. It exposes wrappers to some Javascript variables, such as innerWidth, and innerHeight which you can use in binding expressions. You can manually refresh nodes (only required if they were not generated as declarative). Tag classes expose all of the standard DOM events so that you can do reverse-only bindings on them: e.g. in your SCHTML, for any tag, you can add the attribute: clickEvent="=: myMethod()". This is a nice syntax to avoid the need for function closures or interfaces.

The tag classes match the DOM hierarchy and tag names. They live in the sc.lang.html package: * Node - This tag is used for tags and attributes. * Element, HTMLElement - Used as the base classes for all tags in the page. * Html, Head, Body, Div, Input, A, etc. - There is a Java class for each HTML tag where the first letter is capitalized. They are used as the base class for that tag unless overriden by a class in the tagPackageList. This variable is typically only used for framework layers to provide default content for tags like html, head, or body. For example, the js and servlet layers inject code to specify the script tags required to include all necessary JS files. Because these are classes in StrataCode, you can modify them to customize the content for your application without having to change each page.

Tag Bindable Properties

StrataCode tag objects are very powerful when used with data binding on these properties:

For the select tag:

For the input tag:

For the img tag:

For the option tag:

TODO: We are still filling in missing pieces in this API. Let us know if you need anything added and we'll add it.

Control Attributes

Here's a summary of the behavioral attributes you can set on any element:

See the tag objects doc for more details on merging and exec.

Document, Location, Window

See these classes in the javadoc: java doc]

There are Java classes which work on both client and server, letting you use some of the features of these Java objects. For example, manipulating the current request's URL. Some of the features exposed in the Java API are only available on the client. For example, the Window.window.innerWidth and innerHeight properties are only set on the client, where they are bindable.

Imports

Just like JSP, you do imports and annotations using the <%@ annotation as the first thing in the file.

<%@
     import myClass;
%>

Annotations

To annotate the class corresponding to the page itself, you use:

---- PageName.schtml:
<%@
    @AnnotationName(annotationValues=...)
%>

Live In-Browser Programming

StrataCode as a framework supports changing the code while the application is running. Just as these changes can sometimes be applied to the server code at runtime without a restart, they can be converted to Javascript code and run on the client. When the changes are more complex, the complete javascript on the server is regenerated and the client page is told to refresh. The server itself may need to restart because of changes to compiled code.