Flexible, strongly typed, mixed client/server web framework

The StrataCode web framework supports an extended form of HTML that gives you the power of the StrataCode language mixed with HTML. Using inheritance, data binding, components, properties, etc. build configurable, rule-oriented, reactive templates. Keep code out of the page, or not based on the situation.

Tags can extend other tags either in code or template form, set properties via attributes, declare new properties, use data binding expressions for attributes or content for incremental refresh. Tags support a 'repeat' attribute which you can set to an array or list. The tag is itself repeated once for each element with a repeatVar you can use to bind to the element.

Declarative/procedural web framework that supports efficient content rendering

  • Readable, toolable HTML templates
  • Data binding for automatic, incremental refresh of content and attributes
  • Set refreshBindings=true on tags which need to have bindings checked because they depend on properties which do not send change events.
  • Same syntax can be used to generate a single 'output' method when the reactive style for a tag is not a match.
  • Unlike many frameworks, you can step into and out of the code in the debugger. You can set breakpoints in your setX or getX methods and walk into and out of data binding executions or the rendering of a template. Why spend your time reading the code and trying to figure it out when you can just have all the state there in front of you at the time the bug occurs. In the browser, tag objects map to DOM nodes, listen for DOM events, and update the DOM as it's properties change. On the server, tags listen for changes from the client using the sync protocol and refresh efficiently after each sync. On both client and server, refresh uses a two-stage process. As properties change, tag contents are invalidated. Later, after all inputs have been applied and all tags revalidated are the tags refreshed. Any unchanged tags can use the cached contents. Changes are refreshed in a single pass. Because you can set breakpoints in set/get methods, the protocols are designed for readability, and there are multiple logging options, you can trace what's going on and find and fix problems.

Advantages

  • Runs on client, server, or both from the same syntax. One code base - different ways to run the same logic for different patterns of code or to optimize download size.
  • For applications which need lots of code to refresh, you can use the server-only model. The client has a very small Javascript file that still provides a fully featured webframework including: real time, flexible test scripts, and other advanced features.
  • For client-heavy applications, efficient incremental updates from Java code using Java libraries (e.g. LinkedHashMap).
  • Isomorphic Java: Fast initial page loads of complex, dynamic pages, followed by incremental page updates.
  • Use server-side rendering for some features, client-side for others and easily change between the two models.
  • Layer templates and code - incrementally customize pages: replace, append, prepend DOM elements, override properties, or methods in a layer to behavior: A/B testing, or general customization.
  • Statically typed for compile time errors, runtime speed and IDE support for all assets - e.g. find all usages of a given tag.

Tag APIs

The default tag types, Body, Form, etc. implement a subset of the Javascript DOM API that works on both the client and the server. For declarative changes, i.e. those which can be converted into data binding expressions, the updates are incrementally refreshed automatically as properties change. If you need to change the HTML yourself in code, you use the DOM api and refreshes again are scheduled automatically. You do not have to choose whether this code runs on the client or server. That will be managed by the frameworks you include when your application is run, or controllable via the exec attribute or the @Exec annotation.

Client/server transparency

Most frameworks require the application code to explicitly partition itself into client and server by separating the code and managing the communication. With StrataCode, framework layers generate code to manage the synchronization of each type. For each type name, a version is generated to run on the client, another on the server and code is injected to manage the synchronization back and forth.

Server layers typically include code that uses database operations and so are tied on the server. Or may include that that must run on the server for security, or may include that's run on both client and server (e.g. form field validation). By simply setting properties that are shared by client layers, you get client/server connectivity for free.

For example, if you define these properties in a layer shared by client and server:

{
   String queryParam
   List results
}

then a server-only layer defines:

{
   results := query(queryParam)
}

when the client sets queryParam, the change is sent to the server on the next sync. The server responds to the change of queryParam, calls the query(queryParam) method and sets the results property to the result. That change is queued to go back to the client in the response to the sync.

To do an insert you can use a reverse binding on the server - to call a method in response to client changes. In the shared layer you have:

{
   firstName, lastName;
   insertCount; // incremented each time the insert button is pressed
}

in the server layer:

{
   insertCount =: insert(firstName, lastName);
}

or to do an update do this in the server layer:

{
   firstName =: update();
   lastName =: update();
}

To change where code runs without using layers, use the @Exec annotation or the corresponding 'exec' attribute of the schtml tag.

To support push, the server can initiate the sync over a web socket or be polled for syncs - no other changes to the model are required.

This same framework will similarly support very efficient integrations with other languages like Php, Python, .NET and IOS without too much effort.

One template language, different ways to evaluate

StrataCode templates can run in several different ways:

  1. Generate a static html file by evaluating the template at build time. This is the default for any template types which are 'global scoped' and can be evaluated at build time.
  2. Client-only mode: Generate a static html file that includes javascript to render in the browser for that page
  3. Client-server mode: Build a class for the template in each of two runtimes from the same set of layers. A java_Server process runs a version to serve the initial HTML when the page is rendered. A js version runs in the browser. At the same time, it can also build/manage the RPC interface between the two using the sync framework.
  4. Server-only mode: Generate a template class on the server only which renders the template in response to server requests. The stags.js file can be used for a small client download which listens for input and select DOM events and uses the same sync protocol to update server. In response, the server sends back DOM updates for an efficient, real time dynamic implementation of pages.

Static HTML Files

When you run the 'scc' command with a set of layers, global templates can be converted to static html files in your doc root. This works great for static pages where all of the info is defined at compile-time but assembled via application code. You can re-run scc periodically to update any files which change using the incremental build features which can easily tie into static file distribution systems for efficient management of periodically changing content that's widely distributed.

Server HTML Files

If you include a server layer (e.g. jetty.schtml), your templates can also be rendered on-demand in response to server requests. In this case, the PageDispatcher matches URL requests to the @URL annotation on the template, or refers to the template directly by the path - e.g. TodoList.html

On the server, you can run your templates in either compiled mode for efficient runtime, or dynamic mode which allows you to change the template and hit 'refresh' in the browser to see the changes. For declarative tag objects (as opposed to stateless ones which are converted to a simple output method), a lot more is going on behind the scenes because of the dynamic runtime. The existing template page type is 'updated' - meaning fields, inner types, etc. are removed or added as necessary. Any instances that exist are patched as necessary. Framework hooks run so that page types are unregistered and registered if their @URL annotation changes. If you need to change a simple formula on a complex page, you need this architecture to support an efficient, flexible "excel like" management UI that allows rapid customization.

Client templates in javascript

A template class can also be converted to a Java class, that's then converted to Javascript and run in the browser. It picks up where the static html file or server rendered HTML left off. It listens for input and DOM events and incrementally updates the tag object, triggering any data binding expressions and running code in the browser. If sync is enabled, it sends those changes in a batch to the server where they are applied to the server objects. Also if sync is enabled, any changes from the server incrementally update the tag objects which then update the browser's DOM model for the web page.

Server only templates in javascript

If you only include jetty.schtml, and not js.schtml the template is rendered on the server only. It's basically the same class by the 'serverTag' flag is also set. In this case, it supports the same features but implemented in a different way. There's only a small javascript file (~10K) that's downloaded, along with some metadata about which DOM events are used on the server. It adds listeners for these events and sends batches to the server. The server similarly sends changes back as updates to the tag objects using the sync protocol. The small JS file parses those changes and updates the DOM. This simple model supports realtime just like the client-server model.

Mixing modes of operation

When your layer set includes both js.schtml and jetty.schtml, you can selectively designate whether to run a given template on the client-only, server-only, or both.

For a template which is rendered in client/server mode, you can designate that rendering switch to either the client mode, or the server mode for a given child. Right now, you can switch from both to client-only or server-only but not back again. This is used for example so that static sections of a template only run on the server, even when you have some dynamic tags to run on the client. It is most commonly used when you have a server-only dependency in a template page that you also want to run on the client. Although I can imagine wanting part of a template to only run on the client, i.e. to display some browser specific info, it hasn't been useful yet.

Life cycles of templates and tag objects

The template can have a scope annotation to define its lifecycle. Each scope defines one or more ScopeContexts - hashtables which store the id to instance mapping for the object instances that use that scope. For templates the object is the instance of the template class generated. The global scope has a single ScopeContext that's used and so everyone shares the same instance. ScopeContexts are stored in a directed graph where a child can inherit from more than one parent.

The coreFramework provides these scopes by default: global (one per server), appGlobal (one per application running on the server), session (one per browser-session), appSession (one per browser-session per app), window (one per each specific application window) and request (one per request or transaction). The scopes are combined to form a single hierarchical, layered name space for state management. This design provides a number of benefits. You can lookup names without knowing their scope, which makes it easier to change the scope a name binds to when you are refactoring, or running the code in a new way. You can trace all of the statements that read or write to a given scope for security auditing - to understand what state is exposed to end users. For debuggability - navigating the scopes, watching property changes, tracing how state moves from client to server and back again. The sharing of scopes is synchronized in the server, and transmits events between clients to provide real time data with flexible, declarative sharing arrangements. You can share data between windows of the same user, or users of the same application, or all users of the server.

StrataCode's Java to Javascript engine

Built on parselets, the Java to JS converter is relatively straightforward implementation, flexible, customizable, and produces readable, usable Javascript code with intuitive type, method and property names. Unlike GWT, you can use this Javascript code from other Javascript easily as it exposes Java-like apis directly in Javascript, using predictable conversion rules and flexible special casing as required for name conflicts. You keep your code in Java or StrataCode using maintainable, statically typed APIs.

Click here for more info on StrataCode's Javascript conversion framework.