FTN7: FutoIn Invoker Concept
Version: 1.6
Date: 2017-08-18
Copyright: 2014-2017 FutoIn Project (http://futoin.org)
Authors: Andrey Galkin

CHANGES

Warning

This specification IS NOT mandatory. It is just a reference model. Any implementation IS ALLOWED to provide own architecture with drawback of breaking easy migration from one to another.

1. Concept

There must be a centralized connection and credentials manager (CCM) object. It should be possible to create multiple independent instances of such type.

Each connection end-point should have a unique associative name in scope of single CCM object. Connection end-points can be statically and/or dynamically registered/configured. More advanced implementation may use centralized service to resolve/retrieve connection end-points and related credentials on demand.

Invoker code retrieves FutoIn interface references from CCM object and invokes FutoIn interface function through special "call" method, passing function name and associative parameter map. Invocation result is returned as a map of result values.

There should be a common FutoIn exception type from which Expected and Unexpected FutoIn exception types must inherit as defined in FTN3.

By design, most Executor implementations also implement Invoker design as it is required for separation of concerns on Service/RPC level, but not single project level.

There are several native events supported using FTN15 Native Event interface

1.1. Reserved interface names

Some of interface names can be reserved for internal semantics, like runtime resolving service interface.

All names starting with hash "#" symbol are reserved for internal purpose in Invoker implementation concept.

1.1.2. Pre-defined interfaces names:

1.2. Type and identifier safety

There should be a Simple CCM, which is lightweight and designed for small memory footprint and/or high performance cases. Advanced CCM interface should inherit from the Simple one.

Advanced CCM implementation must implement a special native dummy interface, even if there is no difference with Simple one, so native link/resolve error is generated, if Simple CCM implementation is provided to Client code, which expects Advanced CCM.

Advanced CCM implementation can retrieve FutoIn interface definitions and enforce additional checks, including compile and/or run-time specialized native interface building.

In such process, FutoIn interface functions must become native interface members with every function parameter becoming native member's formal parameter. If multiple values can be returned natively, result values are mapped to those. Otherwise, map of result values is returned. If there are no result values then native member returns nothing and should complete as soon as request message is scheduled to be sent without waiting for reply.

Note: Simple CCM is not expected to parse interfaces. Therefore, all request messages must have "forcersp" flag and Simple CCM must expect response for every call.

1.3. Invoker calls to Executor in scope of the same process

FutoIn implementations are allowed to optimize calls within single process in implementation-defined way, keeping the same behavior between remote and local calls.

Local calls must never execute if there are Invoker frames on execution stack. It means, Invoker function must return before Executor runs or Executor must run in a different thread. Yes, it may have performance issues, but is natural for async programming.

1.3.1. Internal call security

When internal service is used with disabled on-behalf-of setting it does not make much sense to require authorization inside the same process. A special "-internal" credentials were introduced.

If a new interface without "AllowAnonymous" constraint is registered for internal communication channel (in-process in most cases), but without credentials then CCM must automatically use "-internal" credentials for "sec" field.

1.4. Communication Errors

Invoker should transparently handle transitional communication errors with implicit retries.

1.5. HMAC signature support

Please referer to FTN6 Interface Executor Concept for details. HMAC is supported only by AdvancedCCM.

2. Invoker interfaces

Reference Invoker concept is built around FTN12 Async API

2.1. Connection and Credentials Manager

Simple CCM:

  1. event 'register' ( name, ifacever, rawinfo ) - when new interface get registered
  2. event 'unregister' ( name, rawinfo ) - when interface get unregistered
  3. event 'close' - when CCM is shutdown
  4. void register( AsyncSteps as, name, ifacever, endpoint [, credentials [, options] ] )
  5. NativeIface iface( name )
  6. void unRegister( name )
  7. NativeDefenseIface defense() - shortcut to iface( "#defense" )
  8. NativeLogIface log() - returns native API interface as defined in FTN9 IF AuditLogService
  9. NativeCacheIface cache( [bucket="default"] ) - returns native API interface for Cache as defined in FTN14 Cache
  10. void assertIface( name, ifacever )
  11. void alias( name, alias )
  12. void close()

Advanced CCM extensions:

  1. void initFromCache( AsyncSteps as, cache_l1_endpoint )
  2. void cacheInit( AsyncSteps as )

2.1.1. Unique interface name in CCM instance (name)

The idea behind is that each component/library/etc. assumes that end product registers interfaces, their endpoints and possibly provides other information during initialization phase or prior to using the specific component.

Example:

// Init
AsyncSteps as;

as.add( function( as ){
    ccm.register( as, "some_id', "some.iface:1.3", "https://..." )
} )

.add( function( as ){
    // register must complete, before the interface can be aliased
    ccm.alias( "some_id', "componentA.ifaceA" )
    ccm.alias( "some_id', "componentB.ifaceB" )

    ComponentA.init( as );
    ComponentB.init( as );
    startService( as );
} )

// start actual execution
.execute();

// in Component A (note minor version less than registered)
ComponentA.init( as )
{
    ccm.assertIface( "componentA.iface", "some.iface:1.0" )
    iface = ccm.iface( "componentA.iface" )
    iface.someFunc( as )
}

// in Component B (note different minor version)
ComponentB.init( as )
{
    ccm.assertIface( "componentB.iface", "some.iface:1.1" )
    iface = ccm.iface( "componentB.iface" )
    iface.someFunc( as )
}

2.1.2. Interface and version

ifacever must be represented as FutoIn interface identifier and version, separated by colon ":" Example: "futoin.master.service:1.0", "futoin.master.service:2.1".

Invoker implementation must ensure that major versions match and registered minor version is not less than requested minor version.

2.1.3. End point URL

The following URL schemes should be supported:

2.1.4. End point options

2.2. Native FutoIn interface interface

  1. event 'connect' - called on bi-directional channels when connection is established
  2. event 'close' - called when interface is unregistered or CCM shutdown
  3. event 'disconnect' - called on bi-directional channel on disconnect
  4. void call( AsyncSteps as, name, params [, upload_data [, download_stream [, timeout ]]] )
  5. InterfaceInfo ifaceInfo() - return interface to introspect interface information:
  6. void bindDerivedKey( AsyncSteps as )

Advanced CCM:

  1. void _member_call_intercept( AsyncSteps as, param1, param2, param3, ... )

2.3. Derived Key accessing wrapper

The same interface can be used in parallel. This feature generates and binds specific DerivedKey for the following call.

  1. bindDerivedKey( AsyncSteps as )

2.4. Derived Key

See FTN6 Interface Executor Concept

2.5. AsyncSteps interface

See FTN12 Async API

2.5. InterfaceInfo

  1. name() - get FutoIn interface type
  2. version() - get FutoIn interface version
  3. inherits() - get list of inherited interfaces starting from the most derived, may be null
  4. funcs() - get list of available functions, may be null
  5. constraints() - get list of interface constraints, may be null

=END OF SPEC=