Zest™
Introduction
Tutorials
Javadoc
Samples
Core
Libraries
Extensions
Tools
Glossary 

Core API

code

docs

tests

The Zest™ Core API is the primary interface for client application code during the main execution phase, i.e. after the application has been activated.

Table 15. Artifact

Group IDArtifact IDVersion

org.qi4j.core

org.qi4j.core.api

2.1


Composition

Composition is at the heart of COP, and refers to two different levels of constructs;

  1. the ability to assemble (compose) objects from smaller pieces, called Fragments.
  2. the construction of applications by assembling Composites into Modules and Modules into Layers.

In Zest, we use the term Assembly for the second case of composition. See separate chapter.

Composition will allow library authors a new level of flexibility in how functionality is provided to client code. More on that later.

Fragments

There are 4 types of Fragments in Zest;

  • Mixin - The state carrying part of a Composite.
  • Constraint - Rules for in and out arguments, typically used for validation.
  • Concern - Interceptor of method calls. General purpose use, often for cross-cutting behaviors.
  • SideEffect - Executed after the method call has been completed, and unable to influence the outcome of the method call.
Composites

There are 4 Composite meta types. Each of these have very different characteristics and it is important to understand these, so the right meta type is used for the right purpose.

  • Entity - Classic meaning. Has an Identity. Is persistable and can be referenced by the Identity. Can act as Aggregate. Entity supports Lifecycle interface. Equals is defined by the Identity.
  • Value - Values are persistable when used in a Property from an Entity. Values are immutable, and equals is defined by the values of its fields.
  • Service - Service is injectable to other composites and java objects. They are not persistable, but if referenced from an Entity or Value, a new reference to the Service will be injected when the Entity/Value is deserialized. Services are singletons. There are hosted and imported Services. The hosted Service has Configuration and its life cycle controlled by the Zest™ runtime, whereas the imported Services are external references.
  • Transient - Short-lived composites that are not persistable. Equals/hashCode/toString are forwarded to the Mixin Type declaring those methods explicitly.

In versions of Zest™ prior to 2.0 (then Qi4j), composite types had to extend one of these 4 meta types, but in 2.0 and later, the meta type interface is added dynamically during Assembly. We can therefor get rid of a lot of additional types, and use Zest-free interfaces directly;

@Mixins( { BalanceCheckMixin.class } )
public interface BankAccount
{
    Money checkBalance();
      [...snip...]

}

and declare it with;

public void assemble( ModuleAssembly module )
{
    module.entities( BankAccount.class );
}

Structure

Zest™ promotes a conventional view of application structure, that computer science has been using for decades.

The definition is as follows;

  • One Application per Zest™ runtime instance.
  • One or more Layers per Application.
  • Zero, one or more Modules per Layer.
  • Zero, one or more Assemblies per Module.

The principle of this Structure is to assist the programmer to create well modularized applications, that are easily extended and maintained. Zest™ will restrict access between Modules, so that code can only reach Composites and Objects in Modules (including itself) of the same or lower Layers.

Each Layer has to be declared which lower Layer(s) it uses, and it is not allowed that a lower Layer uses a higher Layer, i.e. cyclic references.

Application

Every Zest™ runtime has one and only one Application in it. It is possible to run multiple Zest™ runtimes in the same JVM, and it is even possible to embed a Zest™ runtime within a Zest™ Application, but there can only be one Application in a Zest™ runtime.

An Application is then broken into layers and modules are placed within those layers. Composites are placed within modules. This forms the Application Structure and is enforced by the Zest™ runtime.

Layer

A Zest™ Application must consist of at least one layer. More layers are common, often dividing the application along the common architectural diagrams used on whiteboards, perhaps with a UI layer at the top, followed by a service or application layer, then with a domain layer and finally some persistence layer at the bottom.

Zest™ enforces this layering by requiring the Assembly to declare which layer uses which other layer. And Visibility rules define that layers below can not locate composites in layers above. Also, defining that "Layer1 uses Layer2" and "Layer2 uses Layer3" does NOT imply that Layer1 has Visibility to Layer3. If that is wanted, then it must be declared explicitly.

Module

Modules are logical compartments to assist developers in creating and maintaining well modularized code. A Module only belongs to a single Layer, but many Modules can exist in the same Layer. Composite access is limited to;

  • Composites within the same Module, with Visibility set to Visibility.module (default).
  • Composites from Modules in the same Layer, with Visibility set to Visibility.layer
  • Composites from Modules in Layers below, with Visibility set to Visibility.application

Modules contains a lot of the Zest™ infrastructure, which are the enforcers of these wise modularization principles.

It is not possible to modify the Modules, their resolution nor binding in any way after the application starts.

Visibility

ValueComposite

Usage of value objects is one of the most ignored and best return-on-investment the programmer can do. Values are immutable and can be compared by value instead of memory reference. Concurrency is suddenly not an issue, since either the value exists or it doesn’t, no need for synchronization. Values are typically very easy to test and very robust to refactoring.

Zest™ defines values as a primary meta type through the ValueComposite, as we think the benefits of values are great. The ValueComposite is very light-weight compared to the EntityComposite, and its value can still be persisted as part of an EntityComposite via a Property.

The characteristics of a ValueComposite compared to other Composite meta types are;

  • It is Immutable.
  • Its equals/hashCode works on both the descriptor and the values of the ValueComposite.
  • Can be used as Property types.
  • Can be serialized and deserialized.
Value Serialization

Value objects can be serialized and deserialized using the ValueSerialization API which is a Service API implemented by SPI and extensions.

Tip

ValueSerialization extends ValueSerializer, ValueDeserializer. See the JavaDocs for interfaces detail.

The ValueSerialization mechanism apply to the following object types :

  • ValueComposite,
  • EntityReference,
  • Iterable,
  • Map,
  • Plain Value.

Nested Plain Values, EntityReferences, Iterables, Maps, ValueComposites are supported. EntityComposites and EntityReferences are serialized as their identity string.

Plain Values can be one of :

  • String,
  • Character or char,
  • Boolean or boolean,
  • Integer or int,
  • Long or long,
  • Short or short,
  • Byte or byte,
  • Float or float,
  • Double or double,
  • BigInteger,
  • BigDecimal,
  • Date,
  • DateTime (JodaTime),
  • LocalDateTime (JodaTime),
  • LocalDate (JodaTime).

Tip

Serialization behaviour can be tuned with options. Every ValueSerializer methods can take a ValueSerializer.Options object that contains flags to change how some values are serialized. See the JavaDocs for more details.

Values of unknown types and all arrays are considered as java.io.Serializable and by so are (de)serialized to (from) base64 encoded bytes using pure Java serialization. If it happens that the value is not Serializable or the input to deserialize is invalid, a ValueSerializationException is thrown.

Methods of ValueSerializer allow to specify if the serialized state should contain extra type information about the serialized value. Having type information in the serialized payload allows to keep actual ValueComposite types and by so circumvent AmbiguousTypeException when deserializing.

Core Runtime provides a default ValueSerialization system based on the org.json Java library producing and consuming JSON.

Let’s see how it works in practice.

public interface SomeValue // (1)
{

    Property<String> foo();
}

@Override
public void assemble( ModuleAssembly module )
    throws AssemblyException
{
    module.values( SomeValue.class ); // (2)
      [...snip...]

}
  [...snip...]

public void defaultValueSerialization()
{
    SomeValue someValue = someNewValueInstance( module ); // (3)
    String json = someValue.toString(); // (4)
    SomeValue someNewValue = module.newValueFromSerializedState( SomeValue.class, json ); // (5)
      [...snip...]

}

Reading this first example step by step we ;

  1. declare a ValueComposite,
  2. assemble it,
  3. create a new Value instance,
  4. use the ValueComposite#toString() method to get a JSON representation of the Value,
  5. and finally, use the Module#newValueFromSerializedState() method to create a new Value instance from the JSON state.

ValueComposite#toString() method leverage Value Serialization and by so provide JSON based representation. The Module API allows to create new Value instances from serialized state.

On top of that, Application assemblies can register different implementation of ValueSerialization as Services to support more formats, see the Extensions section. Note that the default behaviour described above is overriden if a ValueSerialization Service is visible.

Let’s see how to use the ValueSerialization Services.

public interface SomeValue // (1)
{

    Property<String> foo();
}

@Override
public void assemble( ModuleAssembly module )
    throws AssemblyException
{
    module.values( SomeValue.class ); // (2)
    new OrgJsonValueSerializationAssembler().assemble( module ); // (3)
}
  [...snip...]

@Service
private ValueSerializer valueSerializer; // (4)
@Service
private ValueDeserializer valueDeserializer; // (4)

  [...snip...]

public void assembledDefaultServiceSerialization()
{
    SomeValue someValue = someNewValueInstance( module ); // (5)
    String json = valueSerializer.serialize( someValue ); // (6)
    SomeValue someNewValue = valueDeserializer.deserialize( SomeValue.class, json ); // (7)
      [...snip...]

}

In this second example, we ;

  1. declare a ValueComposite,
  2. assemble it,
  3. assemble a ValueSerialization Service backed by the org.json package,
  4. get the ValueSerializer and ValueDeserializer Services injected,
  5. create a new Value instance,
  6. use the ValueSerializer#serialize() method to get a JSON representation of the Value,
  7. and finally, use the ValueDeserializer#eserialize() method to create a new Value instance from the JSON state.

Many applications need to stream data. The ValueSerialization API support such use cases in two ways.

The first one use classic streams.

public void assembledServiceStreamingSerialization()
{
  [...snip...]

    // (1)
    Iterable<AcmeValue> data = dataSource; // Eg. Entities converted to Values
    OutputStream output = targetStream; // Eg. streaming JSON over HTTP

    // (2)
    valueSerializer.serialize( data, output );
      [...snip...]

    // (3)
    InputStream input = sourceStream; // Eg. reading incoming JSON

    // (4)
    List<AcmeValue> values = valueDeserializer.deserialize( CollectionType.listOf( AcmeValue.class ), input );
      [...snip...]

}
  1. get a handle on a source of values and an OutputStream,
  2. serialize data into the OutputStream,
  3. get a handle on an InputStream,
  4. deserialize data from the InputStream.

The second one use the I/O API:

public void assembledServiceIOSerialization()
    throws IOException
{
  [...snip...]

    // (1)
    Iterable<AcmeValue> queryResult = dataSource; // Eg. Entities converted to Values
    Writer writer = outputWriter; // Eg. to pipe data to another process or to a file

    // (2)
    Function<AcmeValue, String> serialize = valueSerializer.serialize();

    // (3)
    Inputs.iterable( queryResult ).transferTo( Transforms.map( serialize, Outputs.text( writer ) ) );
      [...snip...]

    // (4)
    Reader reader = inputReader;
    List<AcmeValue> values = new ArrayList<AcmeValue>();

    // (5)
    Function<String, AcmeValue> deserialize = valueDeserializer.deserialize( AcmeValue.class );

    // Deserialization of a collection of AcmeValue from a String.
    // One serialized AcmeValue per line.
    // (6)
    Inputs.text( reader ).transferTo( Transforms.map( deserialize, Outputs.collection( values ) ) );
      [...snip...]

}
  1. get a handle on a source of values and a Writer,
  2. prepare the serialization Function,
  3. serialize a collection of values, one serialized value per line,
  4. get a handle on a serialized values Reader and create a new empty List of values,
  5. prepare the deserialization Function,
  6. deserialize a collection of values from read lines.

Service Composite

Any service added, via the ModuleAssembly.addServices(), ModuleAssembly.services() and ModuleAssembly.importServices() methods, will have the ServiceComposite meta type added to it. In Zest, when we speak of Services we mean instances of ServiceComposite.

Most programmers are familiar with the term "Service", and after the failure of Object Oriented Programming’s promise to encapsulate all the behavior together with the object’s state, programmers learned that the only way to deal with decoupling and re-use was to make the objects into data containers and deploy services that acted upon those data containers. Very much what functions did on structs back in the C and Pascal days.

Zest™ will bring a lot of the behavior back to the Composite itself, but we still need Services for cross-composite functionality. The Zest™ Service model is fairly simple, yet powerful and flexible enough to accommodate most service-oriented patterns and ability to integrate well with external systems whether they are in-JVM or remote, such as Spring, OSGi, WS-*, Rest and others.

The characteristics of a ServiceComposite compared to other Composite meta types are;

  • It is one singleton per declaration in bootstrap.
  • It has an identity defined in bootstrap.
  • It has an Activation life cycle into which Activators hook.
  • It has an optional Configuration.

Services in Zest™ are singletons, one instance per definition. That means that there may exist multiple instances of the same service type, but they can not be created on the fly in runtime, but has to be explicitly defined during Assembly.

By default, Services are not instantiated until they are used. This means that the ServiceComposite instance itself will not exist until someone calls a method. If a Service needs to be instantiated when the Module is activated, one need to declare/call the instantiateOnStartup() method on the ServiceDescriptor during the bootstrap.

Service Configuration

The configuration for a service is well supported in Zest. See the Service Configuration chapter for details.

Service Activation

Services are activated (injected and instantiated) either on application start-up, or upon first use. This is controlled by calling instantiateOnStartup(), this way;

@Override
public void assemble( ModuleAssembly module )
    throws AssemblyException
{
    ServiceDeclaration service = module.addServices( MyDemoService.class );
    service.instantiateOnStartup();

If this method is not called during assembly, the activation will occur on first service usage.

Passivation occurs when a Module is deactivated, typically because the whole application is shutting down. Passivation occurs in the reverse order of the activation, to ensure that dependent services are still available for a passivating service.

Activators can be assembled with Services to manage their activation. The easiest way is to implement the ServiceActivation interface directly in the ServiceComposite;

@Mixins( MyActivationMixin.class )
public static interface MyActivationDemoService
    extends ServiceComposite, ServiceActivation
{
}

public static class MyActivationMixin
    implements ServiceActivation
{
    @Override
    public void activateService()
        throws Exception
    {
        // Activation code
    }

    @Override
    public void passivateService()
        throws Exception
    {
        // Passivation code
    }
}

The activation code can also be moved outside the composite by using the ServiceActivatorAdapter;

@Activators( MyActivator.class )
public static interface MyOtherActivationDemoService
    extends ServiceComposite
{
}

public static class MyActivator
    extends ServiceActivatorAdapter<MyOtherActivationDemoService>
{
    @Override
    public void afterActivation( ServiceReference<MyOtherActivationDemoService> activated )
        throws Exception
    {
        // Activation code
    }

    @Override
    public void beforePassivation( ServiceReference<MyOtherActivationDemoService> passivating )
        throws Exception
    {
        // Passivation code
    }
}

Activators can be registered on Service assembly too, this way;

@Override
public void assemble( ModuleAssembly module )
{
    module.services( MyDemoService.class ).withActivators( MyActivator.class );
}

Activators assembled with the service will get their beforeActivation and afterActivation methods called around the ServiceComposite activation and their beforePassivation and afterPassivation around the ServiceComposite passivation. Member injection and constructor initialization occur during the activation. The ServiceComposite can be used from the afterActivation to the beforePassivation method.

Identity and Tags

Services has an Identity, which drives the Service Configuration system and can be used to lookup a particular service instance. Services can also be arbitrarily tagged, via the ServiceDescriptor. Example;

@Override
public void assemble( ModuleAssembly module )
    throws AssemblyException
{
    ServiceDeclaration service = module.addServices( MyDemoService.class );
      [...snip...]

    service.taggedWith( "Important", "Drain" );

Tags are useful inside the application code to locate a particular service instance, in case we have many. For instance;

@Service
private List<ServiceReference<MyDemoService>> services;

public MyDemoService locateImportantService()
{
    for( ServiceReference<MyDemoService> ref : services )
    {
        ServiceTags serviceTags = ref.metaInfo( ServiceTags.class );
        if( serviceTags.hasTag( "Important" ) )
        {
            return ref.get();
        }
    }
    return null;
}

Service Configuration

Configuration in Zest™ is for Zest™ ServiceComposite only. The Configuration is stored in a visible Entity Store and is therefor runtime modifiable and not static in properties or XML files as in most other dependency injection frameworks.

The Configuration system itself will handle all the details with interfacing with reading and writing the configuration. The normal UnitOfWork management is used, but handled internally by the configuration system.

In Zest, Configuration are strongly typed and refactoring-friendly. Configuration is read from the entity store, but if it can not be found, then it will try to bootstrap it from a properties file, with the same name as the ServiceDescriptor.identifiedBy(), which is set during Assembly and defaults to the fully qualified classname of the ServiceComposite type.

Defining a Configuration Type

The Configuration type is simply listing the properties that are available. The standard rules on @UseDefaults and @Optional applies. Example;

public interface MailServiceConfiguration extends ConfigurationComposite
{
    Property<String> hostName();

    Property<Integer> port();
}
Using a Configuration Type

It is important to remember that Configuration is not static values that are set prior to application start-up and therefor applications should not cache the values retrieved forever, but consciously know when the configuration should be re-read.

Configuration is injected via the @This injection scope. One reasonable strategy is to read the configuration on service activation, so by deactivating/reactivating a service, the user have a well-defined behavior to know how configuration changes take effect. Example;

@This
private Configuration<MailServiceConfiguration> config;

@Override
public void sendMail( @Email String to, @MinLength( 8 ) String subject, String body )
{
    config.refresh();
    MailServiceConfiguration conf = config.get();
    String hostName = conf.hostName().get();
    int port = conf.port().get();
      [...snip...]

}
Modifying Configuration

Configuration is modifiable, and after the modifications have been made, the save() method on the Configuration type must be called. Example;

    void changeExternalMailService( String hostName, int port );
      [...snip...]

        @Override
        public void changeExternalMailService( String hostName, int port )
        {
            MailServiceConfiguration conf = config.get();
            conf.hostName().set( hostName );
            conf.port().set( port );
            config.save();
        }
          [...snip...]

    }
}

EntityComposite

Entities are common in the object oriented programming world, but has never reached the stardom of Class and Object. Instead we have seen many attempts at creating Entities on top of Java, such as EJB (3 incompatible versions), Java Data Objects (JDO, 2 somewhat compatible versions), Java Persistence Architecture (JPA, 2 somewhat compatible versions), Hibernate (4+ somewhat incompatible versions) and many other less known. This seems to suggest that the topic of creating objects that survives over long periods of time is a difficult one.

Eric Evans points out in his book that Entities is a very definite and distinct concept that needs to be handled explicitly. Composite Oriented Programming in general, and Zest™ in particular, takes this point very seriously and makes Entities a central part of the whole system. And likewise, we are convinced that it is not possible to develop domain-knowledge-rich applications without a conscious and well-defined strategy on Entities. So, instead of spending endless hours trying to get Hibernate mapping to do the right thing, we introduce a Composite meta type called EntityComposite, which all entities must derive from, and by doing so automatically become persistable, searchable, have a lifecycle and support nested undoable modifications.

The characteristics of an EntityComposite compared to other Composite meta types are;

  • It has an Identity.
  • It has a LifeCycle.
  • It is typically persisted.
  • It can only be referenced by an Association or ManyAssociation.
  • Its CRUD operations are bound by a UnitOfWork.

Unit Of Work

A UnitOfWork is a bounded group of operations performed, typically on entities, where these operations are not visible to other threads until the UnitOfWork is completed. It is also possible to discard these operations, as if they were never executed.

Note

UnitOfWork has many similarities with the Transaction concept used with RDBMSes. But since Zest™ introduced several deviations to the common definitions of Transactions, we chose to use a different term.

There are several key characteristics of UnitOfWork;

  • They are limited to a single thread.
  • They have an associated use-case.
  • They can be paused and resumed.
  • They have a notification mechanism (used to trigger Indexing for instance).
  • They can be long-running, as they don’t tie up underlying transactions or other expensive resources.

At the moment, they are exclusively used to manipulate EntityComposite composites. All entity operations MUST be done via UnitOfWork, and in fact it is not possible to get this wrong.

UnitOfWork Propagation

UnitOfWork is associated with a thread, and can only be transferred to another thread by a relatively complex operation of pausing a UnitOfWork in one thread, then hand over the UnitOfWork to the other thread and resume it there. Don’t do it!

UnitOfWork is available from the Module, and from the Module you request either a new UnitOfWork or asking for the _current one. Current UnitOfWork means the UnitOfWork that was created earlier within the same thread. So, typically most entity manipulation code only request the current UnitOfWork and the management of creating, completing and aborting the UnitOfWork is handled by the transaction boundary, often in the so called application layer (see Layer)

Since it is very common to have all, or nearly all, methods in the transaction boundary to handle the creation and completion, possibly with retry, in the same class, module or even layer, Zest™ provides annotations to easily declare UnitOfWork concern: @UnitOfWorkPropagation, @UnitOfWorkDiscardOn and @UnitOfWorkRetry

TransientComposite

TransientComposite is a Composite meta type for all other cases. The main characteristics are;

  • It can not be serialized nor persisted.
  • hashcode/equals are not treated specially and will be delegated to fragment(s) implementing those methods.
  • It can not be used as a Property type.

Mixin

Mixins are the state-carrying part of a Composite instance. The other Fragments can not retain state between method invocations as they are shared across Composite instances.

Mixin Type

The Mixin Type is the interface that declares the Mixin methods. Each Mixin implementation (the classes defined in the @Mixins annotation of a Composite declaration) implements one or more methods from one or more Mixin Types.

Mixin Type can be very simple, like;

public interface BankAccount
{
    Money checkBalance();
}

Or contain hundreds of methods, subclassed from dozens of super interfaces.

The Mixin Types of a Composite are ;

  • all the aggregated interfaces of the Composite Type, minus Composite meta-type interfaces, and
  • all private mixin referenced types.

There is not a 1:1 correlation between Mixin Type and Mixin implementation. One can’t even know if there are more or less of one over the other. That is because a Mixin implementation can implement less than one, one, or more than one Mixin Type.

It is also entirely possible that multiple implementation methods exists for a Mixin Type method. The mixin method resolution algorithm will provide a deterministic behavior of which implementation of a method is chosen. The algorithm is as follows;

For each declared method of all Mixin Types of a Composite;

  • Iterate all Mixin types declared from left to right in the declaration,
  • Iterate all Mixin types of super-interfaces from left to right in the extends clause,
  • Iterate all Mixin types within one interface before succeeding to the next interface,
  • Iterate all super-interface Mixin types before proceeding to the super-interfaces of those,
  • Iterate all Typed Mixin implementations of all super-interfaces, before repeating the algorithm for Generic Mixin implementations,

This means that one Mixin implementation can override a single method that a larger mixin implementation implements together with many other methods. So, just because a mixin implements MixinTypeA.method1() and has an implementation of MixinTypeA.method2(), doesn’t mean that method2() is mapped to that mixin. This is very important to remember. The Envisage tool is capable of visualizing how Mixin Type methods are mapped to implementations.

Public Mixins

Mixins are the state holders of the composite instance. Public Mixins are the mixins that are exposed to the outside world via the CompositeType interface.

Each method in the CompositeType interface MUST be backed by a mixin class.

Mixins are declared as annotations on the composite interface.

@Mixins( SomethingMixin.class )
public interface Something
{}
public class SomethingMixin
        implements Something
{
    // State is allowed.

    public void doSomething()
    {
        // do stuff...
    }
}

In the above sample, the SomethingMixin will be made part of the Something composite.

If we have many interfaces defining many methods, that all must be backed by a mixin implementation, we simply list all the mixins required.

@Mixins( { StartMixin.class, VehicleMixin.class } )
public interface Car extends Startable, Vehicle
{}
public interface Startable
{
    boolean start();
    void stop();
}

public interface Vehicle
{
    void turn(float angle);

    void accelerate(float acceleration);

    // more methods
}

In the example above, the VehicleMixin would need to deal with all methods defined in the Vehicle interface. That interface could be very large, and could be totally independent concerns. So, instead we should use abstract mixins, which are ordinary mixins but are lacking some methods. This is simply done by declaring the class abstract.

@Mixins( { StartMixin.class, SpeedMixin.class, CrashResultMixin.class } )
public interface Car extends Startable, Vehicle
{}

public interface Vehicle extends SpeedLocation, Crashable
{
}

public interface SpeedLocation
{
    void turn(float angle);

    void accelerate(float acceleration);
}
public abstract class SpeedMixin
        implements SpeedLocation
{
    // state for speed

    public void accelerate( float acceleration )
    {
        // logic
    }
}

Above the SpeedMixin only implements the accelerate() method, and Zest™ will only map that method to this mixin. The other method of the SpeedLocation interface is not satisfied as the example is written and will generate a runtime exception.

Private Mixins

Public mixins expose their methods in the composite interface, and this is not always desirable. Zest™ supports Private Mixins, which are only visible within the composite itself. That means that other fragments in the composite can see/use it, but it is not visible to the clients of the composite.

Private Mixins are handled automatically. When Zest™ detects a @This annotation referring to a type that is not defined in the Composite interface, then that is a Private Mixin. The Mixin implementation class, however, must exist in the list of Mixins in the @Mixins annotation. But often, the Private Mixin only list internal Property methods in the Mixin Type, which will be satisfied by the standard PropertyMixin and hence always available.

This is particularly useful in Domain Driven Design, where you only want to expose domain methods, which are defined by the context where they are used. But the state of the Mixin should not be exposed out at all. For instance, if we have the Cargo interface like;

@Mixins( CargoMixin.class )
public interface Cargo extends EntityComposite
{
    String origin();

    String destination();

    void changeDestination( String newDestination );

}

The interface is defined by its context, and not really exposing the internal state. So in the implementation we probably do something like;

public abstract class CargoMixin
        implements Cargo
{
    @This
    private CargoState state;

    public String origin()
    {
        return state.origin().get();
    }

    public String destination()
    {
        return state.destination().get();
    }

    public void changeDestination( String newDestination )
    {
        state.destination().set( newDestination );
    }
}

public interface CargoState
{
    Property<String> origin();
    Property<String> destination();
}

So, in this typical case, we don’t need to declare the Mixin for the CargoState, as it only defines Property methods, which are handled by the standard PropertyMixin always present.

Typed Mixin vs Generic Mixin implementations

Mixins, Concerns and SideEffects can either be "typed" or "generic". A Typed Mixin implementation implements one or more Mixin Type interfaces, and one or more of the methods of those interfaces. A Generic Mixin implementation implements java.lang.reflect.InvocationHandler, and can therefor be matched to any method of any interface. Typically, AppliesTo annotation is used to filter the methods that such Generic Mixin implementation is mapped against, and sometimes Generic Mixin implementations are "last resort".

Experience shows that Generic Mixin implementations are rare, and should only be used in extreme cases. They are less frequent than Generic Concern or Generic SideEffect implementations, but inside the Zest™ API are a couple of Generic Mixin implementations that are always present to make the life of the developer easier, such as PropertyMixin, AssociationMixin, ManyAssociationMixin, NoopMixin. The first 3 are declared on the Composite and EntityComposite interfaces and automatically included if needed. They also serve as excellent example of what they can be used for.

@AppliesTo( { PropertyMixin.PropertyFilter.class } )
public final class PropertyMixin
    implements InvocationHandler
{
    @State
    private StateHolder state;

    @Override
    public Object invoke( Object proxy, Method method, Object[] args )
        throws Throwable
    {
        return state.propertyFor( method );
    }

    /**
     * Filter Property methods to apply generic Property Mixin.
     */
    public static class PropertyFilter
        implements AppliesToFilter
    {
        @Override
        public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> modifierClass )
        {
            return Property.class.isAssignableFrom( method.getReturnType() );
        }
    }
}

Other examples that we have come across;

  • Mapping from Property<type> to POJO style "properties".
  • Remote Service delegation.
  • Scripting delegation, where a script will implement the Mixin Type.

which seems to indicate that Generic Mixin implementations are likely to be used in integration of other technologies.

Typed Mixin implementations are much preferred in general business logic, as they will be first-class citizens of the IDE as well, for navigation, find usage, refactoring and many other common tasks. This is one of the main advantages of the Zest™ way of doing AOP compared to AspectJ et al, where "weaving" is something bolted onto an application’s classes via regular expressions and known naming conventions, which can change in an instance by a developer being unaware of which PointCuts applies to his code.

Concern

Concerns are the equivalent of "around advice" in Aspect Oriented Programming. They are chained into an invocation stack for each Mixin Type method and invoked after the Constraints have been executed. Since they are sitting "around" the Mixin implementation method, they also have a chance to modify the returned result, and even skip calling the underlying Mixin method implementation altogether.

To create a concern, you need to create a class that,

  • implements the Mixin Type (Typed Concerns) or java.lang.reflect.InvocationHandler (Generic Concerns),
  • extend ConcernOf (Typed Concerns) or GenericConcern (Generic Concerns) [1]

You are allowed to modify both the in-arguments as well as the returned value, including throw exceptions if that is suitable, perhaps for post condition checks.

Typed Concern

As mentioned above, concerns that implements the Mixin Type are called Typed Mixins. They are more common in the business domain, and can be used for many important things in the domain model, such as checking post conditions (i.e. ensure that the state in the entire composite is valid), coordinating services, handling events and much more.

Typed Concerns doesn’t have to implement all the methods in the Mixin Type. By making the class abstract and only implementing the methods of interest, Zest™ runtime will subclass the concern (otherwise not valid for the JVM), but the generated methods will never be invoked.

Generic Concern

In classic AOP, all advice are effectively generic. There is no type information in the advice implementation and the pointcut can be defined anywhere in the code, and the implementation uses proxy InvocationHandlers. Zest™ supports this construct as well, and we call it Generic Concern.

Generic Concerns will be added to all methods that the AppliesToFilter evaluates to true. By default, that is all methods.

AppliesToFilters is a mechanism to limit, or direct, which methods that the concern should be added to. You have full control over this selection process, via several mechanisms.

  • @AppliesTo annotation can be put on the concern, with either;
  • an interface for which the methods should be wrapped, or
  • an AppliesToFilter implementation that is consulted during building the invocation stack, or
  • an annotation type that must be given on the method.
  • Concerns are added only to composites that declares the Concern, either in
  • the Composite Type, or
  • during assembly in the withConcerns() method.

This means that we can make the following three samples of concerns. First the generic concern that is added to the methods of the JDBC Connection class;

@AppliesTo( java.sql.Connection.class )
public class CacheConcern extends GenericConcern
    implements InvocationHandler
{

We can also use an AppliesToFilter to define which methods should be wrapped with the concern, like this;

@AppliesTo( BusinessAppliesToFilter.class )
public class BusinessConcern extends GenericConcern
    implements InvocationHandler
{
  [...snip...]

public class BusinessAppliesToFilter
    implements AppliesToFilter
{

    @Override
    public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass
    )
    {
        return true; // Some criteria for when a method is wrapped with the concern.
    }
}

And finally an example of how to use annotations to mark indvidual methods for being wrapped by the concern.

@AppliesTo( Audited.class )
public class AuditConcern extends GenericConcern
    implements InvocationHandler
{
  [...snip...]

    @Override
    public Object invoke( Object proxy, Method method, Object[] args )
        throws Throwable
    {
        return null;
    }
}

  [...snip...]

@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.METHOD } )
@Documented
@InjectionScope
public @interface Audited
{
}

Note

Even if a method fulfills the requirement for the concern, if the concern is not declared for the Composite then the concern will NOT be applied.

Invocation Order

The concerns are invoked AFTER all Constraint have been checked. The concerns are executed before the SideEffect are executed in the return path.

The order of execution is defined by the declaration order, interface hierarchy, whether the concern is generic or typed and if they are declared in the interface or declared in the Assembly.

From the perspective of incoming call, i.e. after the <core-api-constraint>> have been checked, the following rules are in place;

  • Typed concerns are invoked AFTER Generic concerns.
  • Concern declared to the LEFT are executed BEFORE concerns to the RIGHT.
  • Concerns in subclasses are executed BEFORE concerns in super-interfaces.
  • Concerns in super-interfaces are executed breadth BEFORE depth.
  • Concerns in different super-interfaces at the same "level" are executed with the concerns declared in super-interfaces left of other super-interfaces first. (TODO: Strange explanation)
  • Concerns declared in interfaces are executed AFTER concerns declared in Assembly.

Constraint

SideEffect

DecoratorMixin

A little known feature is the DecoratorMixin, which allows any object to become a Mixin. This is useful when for instance the initialization of the object to act as a Mixin is complex, or maybe an instance is shared across many Composites. This functionality is only relevant in Transients, and therefor not available in other Composite meta types.

This is done by declaring the DecoratorMixin on the interface, and add the object to be used via the use() method on the TransientBuilder.

The DecoratorMixin will optimize the invocation for generic mixins, to avoid additional cost of reflection. But otherwise the DecoratorMixin is fairly simple

Example

Let’s say that we have a model, FooModel, whose implementation is simply a POJO. Several different views shares this the same model instance, so any changes to the model will notify the views.

We start with the FooModel interface;

public interface FooModel
{
    String getBar();
    void setBar(String value);
      [...snip...]

}

and its implementation is not really relevant for this discussion.

Each of the views looks like this;

@Mixins(View1.Mixin.class)
public interface View1
{
    String bar();

    public class Mixin
        implements View1
    {
        @This
        FooModel model;

        @Override
        public String bar()
        {
            return model.getBar();
        }
    }
}

Note that the mixin is expecting to have the FooModel as being part of the view. This also simplies the mixin, which can for instance add and remove listeners to model updates in lifecycle methods.

But we need an implementation of the FooModel that uses the actual implementation of the FooModel. So we decorate the FooModel with the DecoratorMixin.

@Mixins(DecoratorMixin.class)
public interface FooModel

The DecoratorMixin expects that the implementation is found among the "@Uses" objects, so to create a view we simply do;

public View1 createView1( FooModel model )
{
    TransientBuilder<View1> builder = module.newTransientBuilder( View1.class );
    builder.use( model );
    return builder.newInstance();
}

And there is nothing special in the assembly of this simple example;

@Override
public void assemble( ModuleAssembly module )
    throws AssemblyException
{
    module.transients( View1.class );
    module.transients( View2.class );
    module.transients( FooModel.class );
}

This can now be validated in a small test;

@Test
public void testDecoration()
{
    FooModelImpl model = new FooModelImpl( "Init" );
    View1 view1 = createView1( model );
    View2 view2 = createView2( model );
    assertThat( view1.bar(), equalTo( "Init" ) );
    assertThat( view2.bar(), equalTo( "Init" ) );
    model.setBar( "New Value" );
    assertThat( view1.bar(), equalTo( "New Value" ) );
    assertThat( view2.bar(), equalTo( "New Value" ) );
}

Composite Types Lookup

Composite Types Lookup can occurs when you explicitely lookup for a Composite by Type (ex. ServiceFinder.findService(..) methods), when you ask for an injection or when you create a new composite instance.

All theses type lookup start from a Module, are lazy, cached and obey the Zest™ Visibility rules. Type Lookup works equally accross Composite Types with some subtle differences when it comes to Services and Entities.

Object, Transient and Value Types Lookup

When creating or injecting Objects, Transients or Values the Type Lookup does the following:

First, if Object/Transient/Value Models exactly match the given type, the closest one (Visibility then Assembly order) is returned. Multiple exact matches with the same Visibility are forbidden and result in an AmbiguousTypeException.

Second, if Object/Transient/Value Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned. Multiple assignable matches with the same Visibility are forbidden and result in an AmbiguousTypeException.

Entity Types Lookup

Entity Types Lookup is splitted in two use cases famillies: Creational usecases and Non-Creational usecases.

Creational Entity Types Lookup

This Type Lookup takes place when creating new Entity instances from a UnitOfWork and behave exactly like Object/Transient/Value Types Lookups.

Non-Creational Entity Types Lookup

This Type Lookup takes place when fetching Entities from an EntityStore or writing queries using the Query API. The Type Lookup is different here to allow polymorphic use of Entities and Queries.

First difference is that this Type Lookup returns an ordered collection instead of a single match.

Returned collection contains, in order, Entity Models that:

  • exactly match the given type, in Visibility then Assembly order ;
  • match a type assignable to the given type, in Visibility then Assembly order.

Multiple exact matches with the same Visibility are forbidden and result in an AmbiguousTypeException.

Multiple assignable matches are allowed to enable polymorphic fetches and queries.

Service Types Lookup

Service Types Lookup works as follow:

Returned collection contains, in order, ServiceReferences that:

  • exactly match the given type, in Visibility then Assembly order ;
  • match a type assignable to the given type, in Visibility then Assembly order.

Multiple exact matches with the same Visibility are allowed to enable polymorphic lookup/injection.

Multiple assignable matches with the same Visibility are allowed for the very same reason.

Metrics API

The Zest™ platform defines an advanced Metrics SPI to capture runtime metrics of Zest’s internals as well be used by application code (via this API) to provide production metrics for operations personnel, ensuring healthy state of the applications.

MetricsProvider

There are quite a lot of different Metrics components available, which are instantiated via factories. There is one factory for each component type, to allow for additional components to be created in the future without breaking compatibility in the existing implementations.

The MetricsProvider is a standard Zest™ Service and simply acquired via the @Service annotation on a field or constructor argument.

@Service
private MetricsProvider provider;
Gauge

A Gauge is the simplest form of Metric. It is a value that the application sets, which is polled upon request. The application need to provide the implementation of the value() method. Gauges are genericized for type-safe value handling.

A Gauge can represent anything, for instance, thread pool levels, queue sizes and other resource allocations. It is useful to have separate gauges for percentage (%) and absolute numbers of the same resource. Operations are mainly interested in being alerted when threshold are reach as a percentage, as it is otherwise too many numbers to keep track of.

To create a Gauge, you do something like;

final BlockingQueue queue = new LinkedBlockingQueue( 20 );
  [...snip...]

MetricsGaugeFactory gaugeFactory = provider.createFactory( MetricsGaugeFactory.class );
MetricsGauge<Integer> gauge = gaugeFactory.registerGauge( getClass(), "Sample Gauge", new MetricsGauge<Integer>()
{
    @Override
    public Integer value()
    {
        return queue.size();
    }
} );
Counter
MetricsCounterFactory counterFactory = provider.createFactory( MetricsCounterFactory.class );
MetricsCounter counter = counterFactory.createCounter( getClass(), "Sample Counter" );
Histogram
MetricsHistogramFactory histoFactory = provider.createFactory( MetricsHistogramFactory.class );
MetricsHistogram histogram = histoFactory.createHistogram( getClass(), "Sample Histogram" );
Meter
MetricsMeterFactory meterFactory = provider.createFactory( MetricsMeterFactory.class );
MetricsMeter meter = meterFactory.createMeter( getClass(), "Sample Meter", "requests", TimeUnit.MINUTES );
Timer

Timers capture both the length of some execution as well as rate of calls. They can be used to time method calls, or critical sections, or even HTTP requests duration and similar.

MetricsTimerFactory timerFactory = provider.createFactory( MetricsTimerFactory.class );
MetricsTimer timer = timerFactory.createTimer( getClass(), "Sample Timer", TimeUnit.SECONDS, TimeUnit.HOURS );
HealthCheck
MetricsHealthCheckFactory healthFactory = provider.createFactory( MetricsHealthCheckFactory.class );
MetricsHealthCheck healthCheck = healthFactory.registerHealthCheck(
    getClass(),
    "Sample Healthcheck",
    new MetricsHealthCheck()
    {
        @Override
        public Result check()
            throws Exception
        {
            ServiceStatus status = pingMyService();
            return new Result( status.isOk(), status.getErrorMessage(), status.getException() );
        }
    } );