Services Framework

Document created by resplin Employee on Jun 6, 2015Last modified by alfresco-archivist on Aug 31, 2016
Version 2Show Document
  • View in full screen mode

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com



Core Repository ServicesOverviewJava_API


Introduction


The Services Framework provides a pattern for encapsulating Repository behaviour at the Service, Aspect and Type level.




Requirements


Support the following Repository developer roles:


  1. Service Implementor - responsible for developing new Repository services (and optionally) exposing them as part of the Repository public API
  2. Content Modeler - responsible for defining the structure and behaviour of Content Types
  3. Application Developer - responsible for developing content applications (user of the Repository API and Content Types)

Service Implementor


Service Implementors are Java developers.  The Services Framework provides them a pattern for defining, implementing and exposing the Repository API (i.e. Repository Services).  The pattern promotes an extensible and pluggable Repository architecture.


  1. Definition and registration of public API interfaces
  2. Registration of public API implementations
  3. Service vs. Aspect oriented components
  4. Definition and registration of component policies (callbacks associated with Content Model)
  5. Encapsulation of Content Model behaviour in a component (e.g. policy logic implemented in java)
  6. Registration of internal components (supporting building blocks)
  7. Change existing component behaviour (override or replace)
  8. Implement components in Java
  9. Implement components in script
  10. Dependencies between components
  11. Component specific configuration (XML)
  12. Packaging of services (for install/uninstall)

Content Modeller


Content Modellers are knowledgeable of the business domain.  Technical knowledge varies.  From least to most technical there are:


  • end users - define data structures (i.e. types and properties)
  • advanced administrator/users - define data structures and behaviours (i.e. types, properties and business logic)
  • developers - define data structures and behaviours

The Service Framework is not responsible for maintaining Content Type meta data.  Instead, the Type Mechanism component will provide meta data definition and query.  Out of the box, the Repository is pre-installed with Content Type meta data for common content scenarios, such as File System & Collaboration.

The Service Framework interacts closely with Content Type meta data to bring Types to life! That is, it provides a mechanism for tying behaviour to Types.  To support this, the framework provides:


  1. Definition and registration of policies i.e. points for introducing type level behaviour (as defined by a service implementor).  Policies are associated with the meta-model e.g.
    • Class Policy (Type and Aspect)
    • Property Policy
    • Relationship Policy
  2. Registration of behaviour (business logic) for Type, Property and Relationship policies
    1. Developed in script (declarative registration)
    2. Developed in Java (programmatic registration)
  3. Polymorphic invocation of behaviour based on Content Type
    1. Override behaviour
    2. Access to super.behaviour (Could?)
  4. Access to Policy meta-data for tool support

Application Developer


Application developers are technically oriented.  However, the Repository should provide access for all types of application developers - enterprise, LAMP and .NET.

The Service framework allows:


  1. The discovery of and access to Repository public services (APIs)
  2. Consumption of Repository Services by a number of languages
    • Java (primary)
    • Scripting Language
    • BPEL
    • PHP
  3. Availability of Repository Services in
    • Portal
    • Application Server
    • Web Container
    • Stand-alone Application

Service Framework Model


Service Framework Model


Services


The public Repository API is a collection of service oriented interfaces.  Each service is named and has an associated Java interface.  Each Repository exposes a Service registry allowing a client to examine the list of available services.  A core set of well-known services are packaged by default.  New services may be deployed extending the capabilities of the Repository.  Clients access a service by name.

Note: A service oriented API (as opposed to a fully object-oriented API) provides a relatively simple interface to Repository services (similar to WebDAV).  It lends itself towards a stateless approach, and as such, each method on an interface requires all state to be passed in e.g. any objects to operate on.

Service interfaces are backed by an implementation component.  Typically, this is a Java class, but could also be script.  The Service registry identifies the implementation component for each service.  This indirection allows the implementation of any service to be switched without client knowledge.

A component may also be implemented as an internal Repository building block.  In this case, it is not registered with any public Service, but can be still be referred to internally.

Components depend on other components; dependencies are resolved via dependency injection.


Component Type


There are two different type of Component: ServiceComponent and ClassComponent.  Either can be exposed a public Repository Service.


Service Component


A Service Component is one which is not directly tied to any particular class in the meta model e.g. Node_Service (Object CRUD) or Search.


Class Component


A Class based Component is one which is tied to a specific class in the meta model.  The class may be a Type or Mixin, although it's much more useful for Mixin.  The exposed component API provides class specific behaviour e.g. Folder, Version or Lock.  When tying a Component to a Mixin, it's possible to provide automatic introduction of the Mixin to any Nodes that are operated on by the Component.  This is controlled by the ClassBinding value in the above diagram:


  • Check - if the Node does not support the Mixin, report an error
  • Apply - if the Node does not support the Mixin, introduce it to the Node before invoking the Service implementation
  • Defer - let the implementation component decide on the outcome

Note: The apply behaviour can only be supported by a Repository whose implementation supports the introduction of a Mixin to a node at run-time.


Component Policies


Component implementations may defer to Content Types to allow for model specific behaviour to be injected into their process.  The component implementor is responsible for the contract which describes how to inject and when to inject.  This contract is known as a 'policy'.  For example, the Node component may define policies such as onCreateNode, beforeDeleteNode, onUpdateProperties, etc.  The CheckOutCheckIn component may define policies such as onCheckOut, onCheckIn, onCancelCheckOut, etc.

Each policy is named extends one of the following interfaces: ClassPolicy, AssociationPolicy or PropertyPolicy.  The contract is defined via the 'Policy' Java Interface.

The registration of policies is performed during Component initialisation.  An internal Repository component (PolicyComponent) provides the interface for registration.

Note: It is unlikely that there will be a persistent representation of policies.  A Repository can be queried for a list of registered policies, but that list will depend on the initialised components/services.  Content Type meta data (including behaviour) will be persisted and may be transferred from one Repository to another.  It's possible for a Content Type to support a behaviour for a policy which is not registered (because it does not support the service which defines the policy).  In this case, the Repository will ignore the behaviour.


Model Behaviours


Model behaviours are snippets of logic which provide Content Type specific behaviour for a given policy.  The logic may be encapsulated within a Java class (which supports the policy defined Java interface) or script.  Service implementors are likely to provide Java implementations, whilst Content Modellers are likely to provide Script implementations.

In the scripting case, the script must provide the appropriate function(s) to fulfill the Java interface contract.  JSR 223 (and BSF) provide mechanisms to present a Java interface facade on top of a script.  Note: It is not clear if the Content Modeller will need to provide the complete function definition in the script, or whether the Repository can generate a function wrapper around their hand-coded logic.

Behaviours may have dependencies on Repository Services.  Java developers can use standard Dependency Injection techniques.  Script developers can be provided with well known global variables; one for each published Repository Service.

Registration of behaviours is performed through an internal Repository component (PolicyRuntimeService).  This can be achieved programmatically or driven by Content Type meta data developed by hand or generated by an administration tool.  The PolicyRuntimeService is also responsible for dynamically dispatching to the appropriate behaviour based on Content Class (Type and Aspect) and in the future, possibly other context values.


Internal Class Components


Not every component is exposed as a public service, and this is true for a Class based component too.  An internal class component allows for default type specific behaviours to be encapsulated and registered with the Repository (during the class component initialisation).  Typically, a Java implementation is chosen, allowing for sophisticated behaviours (which rely on obscure Java APIs or internal Repository components).  Each behaviour could be implemented as an inner class.

This technique is particularly useful for Aspects which do not expose a specific public Repository API, but do encapsulate behaviour.  For example, Dublin Core or Auditable.  Each of these Aspects have a corresponding Mixin which define a set of attributes.  The internal Class component can register behaviours for policies such as onChanged etc which automatically assign values to lastUpdatedDate or changedBy attributes as defined by the Mixin.

Default behaviours defined by Class components can still be replaced by scripted versions as defined by less technical Content Modellers.


Remote Services


By taking a Service oriented approach to our API, it is relatively easy to provide remote access to the Repository.  In some cases, services may be remotable without change, but others may have to expose a simple facade to improve latency or serialization issues.

Exporting a Web Service layer allows for the Repository to be used in a much wider set of scenarios such as LAMP integration or business process integration.


Content Meta Data


The Content Type meta data schema has yet to be defined, but the assumption is that something similar to Paul's XML definition will exist. 

There is a loose dependency on the Content meta-meta model.  An assumption is that concepts such as Type def, Property def and Relationship def exist.  The existence of such concepts will drive the Policy types available in the framework.


Store Protocols and Node References


Node References and Store Protocols


Service Registry (Public)


The Service Registry provides a means for defining and discovering Repository services.  Effectively, it's the equivalent of UDDI in the Web Services world.


Interface ServiceRegistry


 /**
  * Get Services provided by Repository
  *
  * @return  list of provided Services
  */
List<string> getServices();


/**
  * Is Service Provided?
  *
  * @param serviceName  name of service to test provision of
  * @return  true => provided, false => not provided
  */
boolean isServiceProvided(string serviceName);


/**
  * Get Service Meta Data
  *
  * @param serviceName  name of service to retrieve meta data for
  * @return  the service meta data
  */
ServiceDescriptor getServiceDescriptor( string serviceName );


/**
  * Get Service Interface
  *
  * @param serviceName  name of service to retrieve
  * @return  the service interface
  */ 
Object getService(string serviceName);


//
// Core Service Locators (typed) 
//
// Note: May return null if service is not provided
//


WorkspaceService getWorkspaceService();


NodeService getNodeService();


ContentService getContentService();


SearchService getSearchService();


VersionService getVersionService();


LockService getLockService();

Class ServiceDescriptor


 string  Service Name
class   Service Interface Class
string  Author
string  Version
store[] Supported Store Protocols??

Access to ServiceRegistry


  • Spring Application Context...
 ServiceRegistry registry = applicationContext.get('ServiceRegistry');

  • Programmatically...
 ServiceRegistry registry = new DefaultServiceRegistry(BeanFactory serviceFactory);

Policy Component (Internal)


Policy Component


Spring Support


The Service Framework can utilise Spring as follows:


  • Define components as Beans
    • Wire up dependencies between components
    • XML definition of component properties
    • Java or script implementation
    • Swappable Implementations
  • Attach Transactional behaviour to components
  • Attach Access Control to Service Interfaces
  • Provide a Service Registry on top of a selected number of Beans
  • Export Services via remote protocols (including SOAP)
  • Use Aspects for concepts such as ClassBinding (e.g. introduce mixin automatically on service call)

Definition schema


Initially, the definition of Repository components/services etc will be expressed using Spring syntax.  This will effectively expose Spring to the Service Implementor role.  However, with the introduction of Spring 1.2? (XML schema support) it will be possible to define a Content Repository specific XML schema for the definition of components and services.  At this point, it won't be necessary to expose the Spring dependency to our development community.


Service (NodeService)


An example generic service (i.e. not tied to a specific class definition)...

 Name: Node
Interface: org.alfresco.repository.NodeService (java)
Implementation: DefaultNodeService (name of component implementing the interface)

Example interface (see Node Service API Design for detail)...

 id createNode(workspace, parent, name, type, ...)
void setProperties(workspace, node, properties, ...)

Example component:

 Name: DefaultNodeService
Implementation: org.alfresco....NodeServiceImpl (java)
Dependencies: PolicyDefinition, PolicyRuntime

Service (LockAspectService)


An example aspect based service...

 Name: Lock
Interface: org.alfresco.repository.Lock (java)
Implementation: DefaultLockAspect (name of component implementing the interface)
ClassBinding: Lock Aspect Definition + auto apply aspect

Example interface (see Lock Aspect for detail)...

 lock(workspace, node, owner, ...)
release(workspace, node, ...)

Example component:

 Name: DefaultLockAspect
Implementation: org.alfresco....LockAspectImpl (java)

Policy Definition


Some example Policies exposed via the Node Service implementation...

 Name: onCreate
Type: Class
Interface: org.alfresco.repository.NodeService.CreatePolicy

Name: beforePropertyChanged
Type: Property
Interface: org.alfresco.repository.NodeService.BeforePropertySetPolicy


Example CreatePolicy interface...

 void onCreate(workspace, node, ...)

Example BeforePropertySetPolicy interface...

 void beforePropertySet(workspace, node, property, newvalue, ...)

Example registration of policies (by NodeService implementation)...

 public init()
{
    // policyDefinitionComponent set via Dependency Injection
    policyDefinitionComponent.registerClassPolicy(this, CreatePolicy.class);
    policyDefinitionComponent.registerPropertyPolicy(this, BeforePropertySetPolicy.class);
}

Behaviour (File beforePropertyChanged of Name)


Example declarative registration of (scripted) behaviour (note: the exact form of registering Content Type meta data has yet to be defined)...

  <type name='File'>
     <property name='name'>
        ...
        <beforePropertyChanged>
           if newValue.contains('.')
             throw new Exception('File name ' + newValue + ' is invalid - it cannot contain a full stop');
        </beforePropertyChanged>
     </property>
  </type>

The above declaration expands to the following registration...

  policyRuntimeComponent.registerPropertyBehaviour(type.File, property.name, new ScriptedBehaviour(...));

Example invocation of behaviour from service implementation...

  BeforePropertyChangedPolicy behaviour
     = policyRuntimeComponent.getPropertyBehaviour('beforePropertyChanged',
            node | type, property);
  behaviour.beforePropertySet(workspace, node, property, value);

Note: We could possibly make use of Java 1.5 generics to support the above registration and retrieval of Policy specific interfaces.


Internal Class Component (Auditable)


Example of an Aspect based component which itself does not expose a public interface, but does provide default behaviour for the aspect...

 Name: DefaultAuditableAspect
Implementation: org.alfresco.repository...DefaultAuditableImpl (java)
ClassBinding: Auditable Aspect definition

Example implementation (in java, but could equally be defined in script too)...

 public class DefaultAuditableImpl
{
    // provide setters for dependency injection
    public void setSessionManager(SessionManager sessionMgr) ...
    public void setNodeService(NodeService nodeService) ...
   

    public class AuditableCreatePolicy implements CreatePolicy
    {
       public void onCreate(...)
       {
          nodeService.setProperty(workspace, node, 'createdDate', now);
          nodeService.setProperty(workspace, node, 'createdBy', sessionMgr.getOwner());
       }
    }
   

    public class AuditableSetPropertyPolicy implements SetPropertyPolicy
    {
       public void onSetProperty(...)
       {
          if (property.name != 'updatedDate')
            nodeService.setProperty(workspace, node, 'updatedDate', now);
       }
    }
   

    public class AuditableIntroducePolicy implements IntroducePolicy
    {
       public onIntroduction(...)
       {
          nodeService.setProperty(workspace, node, 'auditStartDate', now);
       }
    }
   

    public void init()
    {
       policyRuntimeComponent.registerClassBehaviour(type.Auditable, new AuditableIntroducePolicy());
       policyRuntimeComponent.registerClassBehaviour(type.Auditable, new AuditableCreatePolicy());
       policyRuntimeComponent.registerPropertyBehaviour(type.Auditable, property.*,
              new AuditableSetPropertyPolicy());
    }
}

Attachments

Outcomes