StrataCode Components

StrataCode has several features to make Java better at building 'components'. This is an overloaded term, but here we use it to mean wired together graphs of objects with the goal of mostly declarative, statically typed applications.

Most Java programmers have encountered component frameworks that implement an inversion of control pattern to wire together a graph of objects for later customization (e.g. Spring, Guice). With StrataCode, these features are all implemented using code-pre-processing, so that a similar version of the original Java file is generated, compiled and run. There's no hidden code injected using byte-code enhancement, runtime binding, startup overhead, or libraries required. The new features are:

While the syntax changes are small, framework developers can plug into this framework to customize how specific types of components are built. For example, a framework developer can customize the code generated for a specific type of 'object' instance like how in the swing framework the inner component automatically adds itself as a child of the parent. With StrataCode components, you can use the defaults, or enable/disable features using annotations, or specify a new code-template for a class replacing the default.

Framework developers can also provide implementations of the IDynChildManager and IDynObjManager interfaces to support 'dynamic components'. These apis to allow your application to manage dynamic collections of components, where you can change the code on-the-fly for better tooling around customization. For example, in the swing framework in a dynamic layer, if you refresh a file in which an inner object has been added or removed, the appropriate addChild/removeChild call is made for live updates. The same thing could be used to update a database schema or other stateful representation that corresponds to a type in the system.

Simplifying Component Oriented Design

Traditional inversion of control frameworks (IOCs) require the developer to design the "configuration interface" as well as the API. Should a feature be configurable in XML, or only accessible from Java? Often this choice is related to whether you want to configure this feature at compile time or runtime.

In IOCs, there's a completely different namespace for components and often a parallel set of names to remember. And for each property there are more questions. Should it be configured in XML or annotations? Is there a default value? At what point during initialization to you perform validation? Can you debug problems?

And for each component, is it acceptable for it to depend on the IOC framework and can we even afford the startup overhead added by use of these frameworks in our application?

Ideally we'd like to make every system configurable, but with IOC adding "configurability" comes with a non-trivial cost that spreads throughout the code. What if it were a simple, incremental process where you can create configuration layers as needed that included just the right properties? You should be able to change a property from not-configurable to configurable without breaking existing configuration by providing a default. If you change a property from being configurable to not-configurable, it should give compile time errors for programs that have configured it. In an IDE, all uses should be discoverable from a 'find usages' operation, fixable quickly in the editor before they become a problem down the road. Any program should be compilable into a standalone jar file with no configuration, or deployed with any subset of its configuration exposed but using static-typing to identify incompatibilities and contain no library dependencies for no overhead.

StrataCode components offer these options. You write your initial code using simple classes, fields or properties in plain old Java. You can use the object keyword in place of class for declarative instances - basically any instance which is a singleton in that context (maybe global, per-session, or per-parent instance for inner objects). You can set the lifecycle of the object declaratively or inherit it from the outer instance.

How do you separate code from configuration at all then? All your code may start out in one layer for prototyping because it's so easy to split layers and how convenient to have all files in one directory when you are writing code for a new project. Because of the static typing model presented by a layer, you can combine or separate intermediate layers as long as you preserve the results expected by the layers you publish. When following this approach, your initial solution is simpler and easier to maintain and evolves to meet needs big or small later. You can split out layers of configuration as needed for any audience.

The layered approach to component assembly provides customization without upfront planning, a solution that improves code reusability substantially.

One of the more important hidden features provided by IOC is the ability to write up graphs of components, even those graphs which contain cycles - i.e. recursive references. When you run into this in a design, you add the @Component annotation to the class and StrataCode changes the way the code is expressed in the generated class to support recursive references and a multi-step initialization phase.

It's not entirely seamless to move from regular Java classes to using StrataCode's @Component annotation, but it's a graceful transition when you find you need it in a design. Many basic classes require no code changes when you add the @Component annotation. Under the hood, processed code that's compiled, any code and any non-trivial references from the constructor or instance initializers are moved to a preInit method. This method is called after the object is constructed and available via it's own getX method. For more complex classes, the developer may need to move code into an 'init()' or 'start()' method, so that dependent references are all initializes to the necessary level (preInit, init, or start).

Since your original code is transformed in this process, maintaining the structure, and comments, it's relatively easy to figure out what's going on in the debugger as you can step through the entire construction process using either the original or generate code.

Creating Object Instances

StrataCode adds to the Java syntax a new keyword: "object" that works a lot like Scala's object keyword. It defines a new variable and creates an instance for that variable automatically when it's accessed or through custom rules defined at code-generation time. A framework developer has control over how the 'object' keyword is converted to Java by customizing templates, and attaching them to a type, base-type or parent-type (where a type can be a class or object). You can also add annotations to the layers which define the objects giving you even more control. Each layer becomes a sandbox designed for customizing a particular type of asset.

Here are a couple of simple examples that show typical code that's generated.

If in "" you have:

object myInstance {
the generated code looks like:
class myInstance {
    private static myInstance myInstance;
    static myInstance getMyInstance() {
        if (myInstance == null)
            myInstance = new myInstance();
        return myInstance;

StrataCode creates a class for the object with a static property which lazily creates the default instance. In your code, you can refer to the object with an ordinary variable e.g. 'myInstance'. At code-generation time, StrataCode will transform that variable into a method call: myInstance.getMyInstance(). We don't synchronize here around the instance creation because framework patterns typically used in StrataCode will synchronize at a higher level. If you need a thread-safe object though, it's a simple change to the code template which you can then configure for a given base-class, for the objects in a specific layer, or globally for all applications using a particular layer.

If you define an inner object, it works like an inner class in Java:

class MyClass {
   object innerObject {

This defines an inner object, with a single instance created for each instance of MyClass. At code generation time, StrataCode generates an instance variable to hold the instance and a similar getInnerObject method inside of MyClass to lazily create and retrieve the instance. As an optimization, StrataCode may not generate a class for each inner object. Many inner objects are just configured instances of some other type and this makes the implementation more efficient.

Framework layers can use inner objects for the parent/child relationship by adding the "setParent" call or constructor parameters during code-generation.


Java programmers have been traditionally advised to avoid exposing fields directly in APIs. Instead the convention used in Java is to define two methods called getX and setX to implement a property called "x". Programmers have to then call the getX or setX method instead of directly manipulating the field. Later, the implementation of the property can change without affecting the calling code. The pattern works well enough, but it's a lot more code to write and makes code less readable.

StrataCode formalizes the Java convention by selectively automating the getX/setX conversion - handling both sides: converting fields to getX and setX methods as needed and converting expressions using fields to use getX and setX method calls as needed. If you define your code with explicit setX/getX methods, you can still just use them as you would Java fields and those references are converted. Your code looks simpler, cleaner and for those cases where getX/setX conversion is not required, you can use fields to avoid those extra method calls. You can use fields knowing that you can eventually change them to getX and setX methods, or have them generated for you later without breaking code.

Frameworks can customize the getX and setX code generated for properties to interact with framework code. You can add logging, tracing, and validation hooks to groups of properties. And unlike byte-code enhancement, you can debug those extensions easily.

Properties With Data Binding

The data binding system detects when properties are used in data binding expressions and generates getX and setX methods with the proper code to implement the binding. The setX method will trigger a change event typically, or the getX method may force the lazy-evaluation of the binding (if that's ever needed). The details of how this happens are managed in framework layers so application programmers and business users only see properties that may be attached to data binding rules. They are provided efficient and powerful abstractions over properties which can adapt from framework to framework.

In most cases, the default is what you need but there are ways to override the automatic getX/setX conversion. You can:

Additionally, the conversion is disabled automatically for a field in it's own getX and setX methods.

Recursive References in Java

Due to the design of the Java language, when you initialize a field with an expression, that expression can not refer to fields whose values are not yet defined - so called "forward references", or "recursive references". If the fields in your object in any way are initialized to values which refer back to the owner object, your code will either not compile or just not work. Component frameworks support recursive references by using a multi-step initialization process. Objects are created and registered in the name space, then references are resolved, then initialization code can run that requires the object to be fully initialized.

Programmers try to avoid recursive references during design for good reasons. They create less modular code, and the multi-step init process is more complex at runtime. But when you need to add one in an evolved design, it can be very hard to refactor code to keep all references going in the same direction. In the real world, data sometimes must flow upstreamand to arrange for the proper API hooks to allow this can be awkward.

That's probably why declarative languages like HTML support recursive references built-in to their syntax. It's more intuitive and less error prone to wire together your components when you can cut and paste around in a file without worrying about the ordering.

This split between "desired syntax" and "language behavior" in Java is thus lacking. We need a great path for evolving code efficiently using one core syntax, that gives the Java developer better tools for supporting components with a more compatible syntax. Today in Java's existing component frameworks, you move configuration back and forth between Java and XML, or at least pick a design that's more configuration heavy than optimal to avoid changing things down the road. That's because most changes will affect published APIs which we want to avoid for most efficient development.

Recursive References in StrataCode using @Component

StrataCode offers a nice solution for the Java programmer. You can add the @Component annotation to your StrataCode class to change the generated Java code to allow that class to use recursive references. StrataCode transforms your Java code to use a multi-step initialization sequence for such classes. First it creates all instances in the reference graph and sets their member variables so the getX methods work. Then the instance variables are assigned. Then initialization code is run for any component classes reachable in the graph from this object.

For simple cases, there are no code changes required once you add @Component. But for complex cases, you may need to move code from your constructor into an init() or start() method that's called alter, once all dependencies are satisfied. Because these methods are all called automatically, you can just define an init() or start() method and put the code there. If your framework already has an "init()" or "start()" method that conflicts, use IAltComponent and override _init() or _start().


Note: Currently the constructor code is run before these reference instance variables are assigned. Should this be fixed? It would be nice if by the time constructor code is run, all instance variables are at least assigned.

It would be nice to be able to configure the list of stages required for a given component class (e.g. init, start, validate, etc.)

Customizing Object Lifecycle

A declarative framework that only supports static objects only goes so far. StrataCode makes the object lifecycle a customization "hinge point" so the same object operator can be used to create applications deployed where instances live with different lifecycles. For example, deploy an application which runs with separate state for each window, versus a shared state keeping all windows for the same session in sync, or all windows sharing the same collaboration session in sync.

Adding a new lifecycle gives a framework developer a powerful new way to reuse declarative applications. To add a new lifecycle, you add code templates to customize the generated getX method for accessing that instance, how fields are synchronized, add init-code for creating a new instance or destroying an instance.

The framework developer can structure layers that provide defaults to make it easy.

Framework layers can control the lifecycle of the page objects by using the scope operator or the @Scope annotation.

For example, the HTML framework provides these scopes:

Scopes are arranged in parent-child relationships, where a scope such as "app-session" can be a child of both "app-global" and "session".

When you use data binding with scopes, you can annotate certain bindings as "cross scope" bindings with @Bindable(crossScope=true). Events for these bindings are queued up before being delivered to the other scope. They would typically be delivered in a different thread, which was operating in the context of that scope. For example, if objects in a session are receiving events from a global object through a data binding, the binding would be marked 'cross scope'.

What this means is that you can build declarative, realtime, and collaborative applications which are thread-safe and efficient.

Note on crossScope: Today, you need to set the crossScope annotation manually but it seems likely in the future we can detect when it should be set automatically. Or at least generate a warning which propagates changes 'up scope' without a crossScope annotation. Also, the cost of crossScope is just another thread-local call to check if the scope has changed, plus the overhead for queuing the change when it has so it's possible we could even make this the default or at least warn when in a non-production environment.