Polygene™
Introduction
Tutorials
Javadoc
Samples
Core
Libraries
Extensions
Tools
Glossary 

Core API

code

docs

tests

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

Table 14. Artifact

Group IDArtifact IDVersion

org.apache.polygene.core

org.apache.polygene.core.api

3.0.0


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 Polygene, 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.

Fragment

Composites should be perceived as single units, although they consist of many Java classes and instances. Some of those Java instances are not even belonging to a particular instance in runtime, but such details can and should be ignored by the developer. Developers should think of the Composite as a single concept, and not be concerned about its internal structure.

The Composite is composed by declaring the parts it forms in the Composite Type interface declaration. Technically speaking, Composite Type is the only Fragment that is required to exist. The other Fragments are optional.

There are 4 types of Fragments in Polygene;

  • 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.

There are one very important thing to know about Fragments;

ONLY Mixins can maintain inter-method state.

That means that Concerns, Constraints and Side Effects can not assume that Java fields between method invocations are preserved. These Fragments must rely on either Mixins or external services for additional state.

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 Polygene™ 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 Polygene™ 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 Polygene-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

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

The definition is as follows;

  • One Application per Polygene™ 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. Polygene™ 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

There is one and only one Application instance per Polygene™ runtime instance. But there is nothing preventing code to create additional Polygene™ Runtime instances inside the same JVM. However, these runtimes are isolated from each other.

The main purpose of the Application structure artifact is to keep everything in the same box, and allowing us to navigate the Structure. So, from a client code perspective, the Application is of no use, other than being part of bring Polygene™ to life. Polygene™ doesn’t start automatically and can be run in most environments, by requiring that the bootstrapping of Polygene™ is done by client code. We call this the Bootstrap Phase. The code in the custom bootstrapper will need to access additional Jars from the regular domain code, and we strongly recommend that you make this separation in your project as well.

Assembly

Assembly is the part of the bootstrap phase where the application Structure is declared (programmatically). The Assembly will be consumed by the ApplicationBuilder, which produces an ApplicationInstance. This instance does not contain any custom objects, and is fully serializable. All the application structure has been built, all the layers and modules been wired up, and all the sub-composite structures are in place to quickly instantiate the various parts of the application.

At this point, where an ApplicationInstance exists, it is possible to initialize the application components with instances created in, data computed in or received from, the controlling bootstrap code.

Once the initialization phase is complete, the bootstrap controller will call the ApplicationInstance.activate() method to start things up.

Recap of sequence;

  • Create, obtain or lookup Assemblers.
  • Establish the application structures.
  • Create a Polygene™ Runtime instance.
  • Create an ApplicationAssemblyFactory.
  • Create an ApplicationFactory.
  • Call ApplicationFactory.newApplication() to create an ApplicationContext.
  • Call ApplicationContext.newApplicationInstance() to create an ApplicationInstance.
  • Do the initialization of the application.
  • Call activate() on the ApplicationInstance.
Singleton Assembler

For really small applications, demos, testcases and so forth, it doesn’t make sense to create a elaborate Application structure. For this purpose, there is a convenient short-cut to establish a single Layer, single Module application. The full code looks like this;

SingletonAssembler polygene = new SingletonAssembler(
    module -> module.values( MyStuffValueComposite.class )
);

Single Module Layering

Behind the scenes of the SingletonAssembler a little bit more elaborate bootstrap sequence is happening. The code below shows what is the actual required sequence to start up Polygene.

    polygene = new Energy4Java();
    applicationInstance = createApplicationInstance();
    activateApplication();
      [...snip...]

private Application createApplicationInstance()
{
    return polygene.newApplication(
        applicationFactory -> applicationFactory.newApplicationAssembly( SingletonAssembler.this )
    );
}

private void activateApplication()
    throws ActivationException
{
    try
    {
        beforeActivation( applicationInstance );
        applicationInstance.activate();
    }
    catch( Exception e )
    {
        if( e instanceof ActivationException )
        {
            throw ( (ActivationException) e );
        }
        throw new ActivationException( "Could not activate application", e );
    }
}

In the above example we are only creating an Application with a single Layer and a single Module in that Layer. This is derived from the fact that the factory.newApplicationAssembly() method takes a single Assembler argument.

The Assembler.assemble( ModuleAssembly assembly ) method is called when the Polygene™ Runtime needs to populate the ModuleAssembly with its Composites, Objects, Services and other information.

"Pancake" Layering

Another standard setup is applications consisting of a small number of Layers that are directly on top of each other (with out bypassing, forking and converging Layers), you can supply a Assembler[][][], with Layer in the first index, Module in the second index and any number of Assembler instances in the last index. This will look like;

final Assembler[][][] assemblers =
    {
        { // web layer
          { // Customer Module
            customerListEditAssembler,
            customerEditAssembler,
            customerSearchAssembler
          },
          { // Accounts Module
            accountsListEditAssembler,
            accountsEditAssembler,
            accountsSearchAssembler
          }
        },
        { // domain layer
          { // Customer Module
            customerDomainAssembler,
          },
          { // Accounts Module
            accountsDomainAssembler,
          }
        }
    };
Energy4Java polygene = new Energy4Java();
Application app = polygene.newApplication( factory -> factory.newApplicationAssembly( assemblers ) );
app.activate();

The array initialization feature in Java is used to give us a semi-visual idea of the actual application structure. It has been commented to highlight this further. Also note that one can pass any number of Assembler instances to each Module. This is an important aspect of subsystem creation and re-use.

Full Layering

Finally, we can assemble the Application by manually building up the Modules and Layers. This allow for a totally free structure, as long as the rules for no cyclic reference of the Layers are kept.

    private static Energy4Java polygene;

    public static void main( String[] args )
        throws Exception
    {
        polygene = new Energy4Java();
        ApplicationDescriptor model = polygene.newApplicationModel( factory -> createAssembly( factory ) );
        Application application = model.newInstance( polygene.spi() );
    }

    private static ApplicationAssembly createAssembly( ApplicationAssemblyFactory factory )
        throws AssemblyException
    {
        String applicationName = "Example Application";
        ApplicationAssembly app = factory.newApplicationAssembly();
        app.setName( applicationName );
        LayerAssembly webLayer = createWebLayer( app );
        LayerAssembly domainLayer = createDomainLayer( app );
        LayerAssembly infraLayer = createInfrastructureLayer( app );
        webLayer.uses( domainLayer );
        webLayer.uses( infraLayer );  // Accesses the WebService
        domainLayer.uses( infraLayer ); // For persistence
        return app;
    }

    private static LayerAssembly createWebLayer(
        ApplicationAssembly application
    )
    {
        LayerAssembly layer = application.layer( "Web Layer" );
        createCustomerWebModule( layer );
        return layer;
    }

    private static LayerAssembly createDomainLayer(
        ApplicationAssembly application
    )
    {
        LayerAssembly layer = application.layer( "Domain Layer" );
        createCustomerDomainModule( layer );
        // :
        // :
        return layer;
    }

    private static LayerAssembly createInfrastructureLayer(
        ApplicationAssembly application
    )
        throws AssemblyException
    {
        LayerAssembly layer = application.layer( "Infrastructure Layer" );
        createWebServiceModule( layer );
        createPersistenceModule( layer );
        return layer;
    }

    private static void createCustomerWebModule( LayerAssembly layer )
    {
        ModuleAssembly assembly = layer.module( "Customer Web Module" );
        assembly.transients( CustomerViewComposite.class );
        assembly.transients( CustomerEditComposite.class );
        assembly.transients( CustomerListViewComposite.class );
        assembly.transients( CustomerSearchComposite.class );
    }

    private static void createCustomerDomainModule( LayerAssembly layer )
    {
        ModuleAssembly assembly = layer.module( "Customer Domain Module" );
        assembly.entities( CustomerEntity.class );
        assembly.entities( CountryEntity.class );
        assembly.transients( AddressComposite.class );
    }

    private static void createWebServiceModule( LayerAssembly layer )
        throws AssemblyException
    {
        ModuleAssembly assembly = layer.module( "Web Service Module" );
        // Someone has created an assembler for a Jetty Web Service.
        JettyAssembler jetty = new JettyAssembler( 8080 );
        jetty.assemble( assembly );
    }

    private static void createPersistenceModule( LayerAssembly layer )
        throws AssemblyException
    {
        ModuleAssembly assembly = layer.module( "Persistence Module" );
        // Someone has created an assembler for the Neo EntityStore
        NeoAssembler neo = new NeoAssembler( "./neostore" );
        neo.assemble( assembly );
    }
      [...snip...]


    public static class CustomerViewComposite
    {

    }
    public static class CustomerEditComposite
    {

    }
    public static class CustomerListViewComposite
    {

    }
    public static class CustomerSearchComposite
    {

    }


    public static class CustomerEntity
    {

    }
    public static class CountryEntity
    {

    }
    public static class AddressComposite
    {

    }

    public static class JettyAssembler
        implements Assembler
    {

        public JettyAssembler( int port )
        {
        }

        @Override
        public void assemble( ModuleAssembly module )
            throws AssemblyException
        {
        }
    }
    public static class NeoAssembler
        implements Assembler
    {

        public NeoAssembler( String s )
        {
        }

        @Override
        public void assemble( ModuleAssembly module )
            throws AssemblyException
        {
        }
    }
}

Layer

A Polygene™ 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.

Polygene™ enforces this layering by requiring the Assembly to declare which layer uses which other layer. And ??? 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 ??? 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 Polygene™ 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.

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.

Polygene™ 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, see Serialization.

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 Polygene, 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.

Polygene™ will bring a lot of the behavior back to the Composite itself, but we still need Services for cross-composite functionality. The Polygene™ 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 Polygene™ 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 Polygene. 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 Polygene™ is for Polygene™ 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 Polygene, 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 the file system, with the same name as the ServiceDescriptor.identifiedBy(), which is set during Assembly and defaults to the fully qualified classname of the ServiceComposite type, followed by an extension dependent on the file type.

The following file types for default configuration is supported (listed in read priority order);

  1. Java Properties
  2. JSON
  3. YAML
  4. XML
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();
}
Default Configuration formats

The default configuration read will happen if the Entity Store backing the Configuration system does not contain the identifiable configuration. That will trigger the reading attempts of the supported configuration formats. Once the configuration is parsed from the file system it is written to the Entity Store, and if the Entity Store is not ephemeral, then on the next start, any changes to the configuration will NOT be detected, and will simply be ignored.

To be able to read JSON, YAML and XML configuration, you must configure a Serialization system that supports the configuration format that you want to use.

  • extension/serialization-javaxjson supports JSON
  • extension/serialization-javaxxml supports XML
Support for Complex Types

Since the regular Value Serialization platform is used, for JSON, YAML and XML, the configuration can contain arbitrary composite types. This is not true for the Java properties file format.

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 Polygene™ 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 Polygene™ 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, Polygene™ 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.

Objects

There are times when Apache Polygene needs to interoperate with other systems, which does not have interfaces as their abstraction. Polygene has a notion of Objects, which are Polygene-managed classes and can still be injected with the Polygene runtime model, such as @Structure and @Service.

The characteristics of an Object compared to Composite meta types are;

  • It is a Class, not an interface.
  • It can have injections applied to it after it has been created.
  • No Mixins, Concerns or SideEffects.
  • No Constraints.
  • Can not have Property instances managed by the Polygene runtime.
Serialization

Objects can be serialized and deserialized using the Serialization API, if and only if they are used as types in Properties in Values or Entities. It depends on the Serialization implementation on how the objects are serialized, and what the requirements are to allow for deserialization. In general, the Spring POJO setter/getter approach will always work, a default constructor is needed, and to be safe, make it into java.io.Serializable.

Usage

Objects are instantiated either by calling ObjectFactory.newObject( type, … ) or instantiating it in some other fashion and then call ObjectFactory.injectInto(…) to populate the fields.

Dependency Injection

Polygene has a rather sophisticated dependency injection system, which is based around the ??? and ??? concepts. The dependency injection system also need help to keep the injection scopes separated. The following injection scopes exists, some more common than others;

  • @This - injection of fragment from the same Composite instance.
  • @Structure - injection of Structure organized types.
  • @Service - injection of services.
  • @Uses - injection of construction injected objects
  • @Invocation - injection of parts related to the current method invocation.
  • @State - injection of state of the composite instance
  • Custom injection scopes - managed through @AppliesTo and AppliesToFilter declarations.
@This

@This is equivalent to the this pointer in the Java language, but refers to any part of the current ???. This can either be a declared mixin type, or if not declared will be a Private Mixin.

We can simply request the injection of any type of the composite that we belong to, such as;

@Mixins( { OrderMixin.class, ShippingMixin.class } )
public interface Order extends HasShippingInformation
{
   :
}

public abstract class OrderMixin
    implements Order
{
    @This
    private HasShippingInformation shipping;
}

But we can have Private Mixin instead, where the injected mixin type will be automatically added to the composite.

@MIxins( OrderMixin.class )
public interface Order
{
   :
}

public class OrderMixin
    implements Order
{
    @This
    private DiscountRate discount;
@Structure

The @Structure injection scope is all about the types involved in the Application Structure system. The possible types are;

  • Application
  • ApplicationDescriptor
  • Layer
  • LayerDescriptor
  • Module
  • ModuleDescriptor
  • ModuleSPI
  • UnitOfWorkFactory
  • EntityBuilderFactory
  • ValueBuilderFactory
  • TransientBuilderFactory
  • ObjectFactory
  • QueryBuilderFactory
  • ServiceFinder
  • PolygeneAPI
  • PolygeneSPI
@Service

Services are injected either in a number of ways, either direct, via List or via ServiceReference types. The following combinations are allowed;

    @Service
    private MyService service;

    @Service
    private Iterable<MyService> services;

    @Service
    private ServiceReference<MyService> service;

    @Service
    private Iterable<ServiceReference<MyService>> services;

If service is not declared instantiateOnStartup during assembly, then the service will be activated on first method invocation, and not on injection. This means that any reflection on the injected instance, may result in unexpected behavior.

@Uses

Object and ValueComposite can be created with uses() declarations. This allows injection of arbitrary types to these meta types. Only type matching will occur, so for instance only one String can be injected this way.

If a @Uses declaration can not be satisfied from the injected objects via uses() builder method, then if the @Uses injection is not @Optional the Polygene runtime will attempt to (in this order)

  • Instantiate a visible TransientComposite
  • Instantiate a visible Object
  • Instantiate a plain object with this Composite instance as a single constructor argument.

If the @Uses is @Optional then no implict object creation will take place.

@Invocation

@Invocation injection scope is all about the current method call. It is possible to inject the following types;

  • The Method being executed.
  • Any annotation type that the method is annotated with.
  • An Iterable of either of the above
@State

This injection scope can inject either a StateHolder which allows inspection of current state of the Composite, or it can inject any declared Property, ???, ManyAssociation or NamedAssociation.

Custom Injection Scopes

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 Polygene™ 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. Polygene™ 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 Polygene™ 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 Polygene™ 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 Polygene™ 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.

Applicability

Concerns are applied to composite types in several ways;

  • @Concerns annotation on the Mixin Type.
  • withConcerns() assembly instruction at bootstrap.
  • In custom annotations to be applied to either Mixin Types or methods on Mixin Types.
  • @Concerns annotation directly on a method.
Typed Concern

As mentioned above, concerns that implements the Mixin Type are called Typed Concerns. 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, Polygene™ 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. Polygene™ 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
  • on any method of the Composite Type, or
  • on an annotation that is in turn declared on a Composite Type method
  • 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

SideEffects have no equivalent in Aspect Oriented Programming. They are executed AFTER the method invocation, and they are potentially concurrent with the method invocation itself. The SideEffect receives the incoming method arguments and can query the result of the method call by accessing the next field. SideEffects can NOT influence the method call in any way, and both return values from the SideEffect, as well as any exceptions thrown, will be ignored.

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

  • implements the Mixin Type (Typed SideEffects) or java.lang.reflect.InvocationHandler (Generic SideEffects),
  • extend SideEffectOf (Typed Concerns) or GenericSideEffect (Generic SideEffects) [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.

Applicability

SideEffects are applied to composite types in several ways;

  • @SideEffects annotation on the Mixin Type.
  • withSideEffects() assembly instruction at bootstrap.
  • @SideEffects annotation of custom annotations to be applied to either Mixin Types or methods on Mixin Types.
  • @SideEffects annotation directly on a method.
Typed SideEffect

As mentioned above, side effects that implements the Mixin Type are called Typed SideEffects.

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

Generic SideEffect

Generic SideEffects implement the java.lang.reflect.InvocationHandler and can potentially serve any method it is applied to. Generic SideEffects 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 side effect, 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.
  • SideEffects are added only to composites that declares the SideEffect, either in
  • the Composite Type, or
  • on any method of the Composite Type, or
  • on an annotation that is in turn declared on a Composite Type method
  • during assembly in the withSideEffects() method.
Invocation Order

The invocation order of SideEffects is UNDEFINED, and one MUST NOT rely on SideEffects executing in any particular order. They MAY be concurrent and outside the thread that executed the method, so the SideEffect can also not depend on the UnitOfWork that may be observed as present.

To be clear; the method call to the SideEffect is NOT its own Polygene-controlled invocation stack, and any annotations on the SideEffect methods will be ignored (or it is a bug). That means that IF the SideEffect needs a UnitOfWork it either needs to manage one explicitly or call out to a service that has the @UnitOfWorkPropagation annotation.

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 = transientBuilderFactory.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" ) );
}

Serialization

State can be serialized and deserialized using the Serialization API which is a Service API implemented by SPI and extensions.

Tip

Serialization extends Serializer, Deserializer. See the JavaDocs for interfaces detail.

The Serialization mechanism apply to the following object types :

  • ValueComposite,
  • EntityReference & Identity,
  • Array, Iterable & Stream,
  • Map,
  • Plain Values.

Nested Plain Values, EntityReferences, Identities, Arrays, Iterables, Streams, 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,
  • java.time types.

Tip

Serialization behaviour can be tuned with options. Every Serializer methods can take a Serializer.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 SerializationException is thrown.

Methods of Serializer 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 Serialization system based on javax.json types.

Let’s see how it works in practice.

public interface SomeValue // (1)
{
    Property<String> foo();
}

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

    module.defaultServices(); // (3)
      [...snip...]

}
  [...snip...]

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

}

Reading this first example step by step we ;

  1. declare a ValueComposite,
  2. assemble it,
  3. assemble default services including default Serialization,
  4. create a new Value instance,
  5. use the ValueComposite#toString() method to get a JSON representation of the Value,
  6. 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 Serialization as Services to support more formats, see the Extensions section. Note that the default behaviour described above is overriden if a Serialization Service is visible.

Let’s see how to use the Serialization Services.

public interface SomeValue // (1)
{
    Property<String> foo();
}

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

    new JavaxJsonSerializationAssembler().assemble( module ); // (3)
      [...snip...]

}
  [...snip...]

@Service
private Serializer serializer; // (4)
@Service
private Deserializer deserializer; // (4)

  [...snip...]

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

}

In this second example, we ;

  1. declare a ValueComposite,
  2. assemble it,
  3. assemble a Serialization Service backed by the javax.json types,
  4. get the Serializer and Deserializer Services injected,
  5. create a new Value instance,
  6. use the Serializer#serialize() method to get a JSON representation of the Value,
  7. and finally, use the Deserializer#deserialize() method to create a new Value instance from the JSON state.

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 Polygene™ 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 Polygene™ platform defines an advanced Metrics SPI to capture runtime metrics of Polygene’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 Polygene™ 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 reached 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( "Sample Gauge", () -> queue.size() );
Counter

Often we want to track the many counters in a system. This might be "number of HTTP requests" or "filesystem access frequency". By creating a Counter metrics, it is simply a matter of calling the increment or decrement on that metric. This will track both number of counts/steps as well as the rate (per second), and many visualization platforms. such as Kibana, Graphite and Grafana, are made to handle this particular metric very well.

MetricsCounterFactory counterFactory = provider.createFactory( MetricsCounterFactory.class );
MetricsCounter counter = counterFactory.createCounter( "Sample Counter" );
Histogram

Histograms is about computing the running standard deviations and variances. Please see "Accurately computing running variance" for more detailed information.

MetricsHistogramFactory histoFactory = provider.createFactory( MetricsHistogramFactory.class );
MetricsHistogram histogram = histoFactory.createHistogram( "Sample Histogram" );
Meter

The Meter is a more advanced Counter, which measures mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.

Wikipedia has a section "Exponential moving average" in the "Moving Average" article.

MetricsMeterFactory meterFactory = provider.createFactory( MetricsMeterFactory.class );
MetricsMeter meter = meterFactory.createMeter( "Sample Meter" );
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( "Sample Timer" );
HealthCheck

HealthCheck is a metric to report the health of a system or component. The HealthCheck metric will be called upon regularly to report its status, which is then forwarded to the monitoring system.

MetricsHealthCheckFactory healthFactory = provider.createFactory( MetricsHealthCheckFactory.class );
MetricsHealthCheck healthCheck = healthFactory.registerHealthCheck( "Sample Healthcheck", () ->
{
    ServiceStatus status = pingMyService();
    if( status.isOk() )
        return MetricsHealthCheck.Result.healthOk();
    String message = status.getErrorMessage();
    Exception error = status.getException();
    if( error != null )
    {
        return MetricsHealthCheck.Result.exception(message, error);
    }
    return MetricsHealthCheck.Result.unhealthy(message);
} );

Timing Capture

A lot of metrics are around the time it takes to execute something and Polygene supports this at its core.

Usage

There are currently the following possibilities available;

  • @TimingCapture - capture timing on a single method
  • TimingCaptureAll - capture timing on all methods of a composite

Before looking at the details of these, we need to point out that there are some pre-conditions for Metrics to be working. First of all, you need to install a Metrics Extensions, most likely the Codahale Metrics Extension. See your chosen extension for details on how to do that.

Once the Metrics extension is installed, you will also need a suitable backend to gather all the data out of a production plant and likewise a good front-end to view this. See your chosen Metrics Extension for this as well.

TimingCaptureAll

There is a TimingCaptureAllConcern, which when added to a composite will install a Timer for every method call in the composite.

@TimingCapture

The @TimingCapture annotation can be placed on any method of the composite, to indicate that a Timer is wanted on that method.

Example;

public interface Router
{
    @TimingCapture
    List<Coordinate> route( String source, String destination );
}

public class RouterAlgorithm1
    implements Router
{
    @Override
    public List<Coordinate> route( String source, String destination )
    {
      [...snip...]

    }
}

public class RouterAlgorithm2
    implements Router
{
    @Override
    public List<Coordinate> route( String source, String destination )
    {
      [...snip...]

    }

      [...snip...]

        @Override
        public void assemble( ModuleAssembly module )
            throws AssemblyException
        {
            module.addServices( Router.class ).identifiedBy( "router1" ).withMixins( RouterAlgorithm1.class );
            module.addServices( Router.class ).identifiedBy( "router2" ).withMixins( RouterAlgorithm2.class );
              [...snip...]

        }
    }
}
Which method?

It is valid to annotate either the composite interface methods or the mixin implementation methods. Any of the method declarations should work. From the testcases we have the following example;

public interface Country extends TransientComposite
{
    @Optional
    Property<String> name();

    void updateName( String newName );
}

@Mixins( Country1Mixin.class )
public interface Country1 extends Country
{
}

public static abstract class Country1Mixin
    implements Country1
{
    @Override
    public void updateName( String newName )
    {
        name().set( newName );
    }
}

@Mixins( Country2Mixin.class )
public interface Country2 extends Country
{
}

public static abstract class Country2Mixin
    implements Country2
{
    @Override
    public void updateName( String newName )
    {
        name().set( newName );
    }
}

@Mixins( Country3Mixin.class )
public interface Country3 extends Country
{
    @TimingCapture( "Country3.updateName" )
    @Override
    void updateName( String newName );
}

public static abstract class Country3Mixin
    implements Country3
{
    @Override
    public void updateName( String newName )
    {
        name().set( newName );
    }
}