Service Domains

Version 5

    Overview

     

    A ServiceDomain represents a collection of services with a shared set of resources, configuration, and policy  definitions.  The ServiceDomain API is used by software components to provide and/or consume services.  These software components include protocol gateways, service containers, translation engines, adapters, orchestration and routing engines.  The ServiceDomain SPI allows dependencies of the ServiceDomain to be abstracted away and set programmatically or via configuration.

     

    service-domain.png

     

    The primary facets of a ServiceDomain include:

     

    • Lifecycle : control and visibility into the state of the domain itself as well as services registered within the domain.

     

    • Configuration : configuration of the domain (e.g. SPI providers) as well as configuration of the services in the domain.

     

    • Policy : policy is a cross-cutting concern that applies to message exchanges between service consumers and providers.  Security requirements, service visibility, and versioning are all examples of policy concerns.

     

    • Service Namespace : represents the set of services that are visible to a domain.  In the case of a single domain, this is the set of services that have been registered with that domain.  If multiple domains share a common service namespace, then the set of services consists of all services registered in each of the domains.

     

    • Dispatch : responsible for coordinating message exchange between service consumer and provider, providing the fundamental runtime decoupling of an SOA runtime.  Multiple providers will be available based on the requirements for dispatch (e.g. shared threads, separate threads, persisted, transactional, etc.).

     

     

    There can be multiple ServiceDomains in a single Java runtime.  The policy and configuration of the domain will determine if and how individual  ServiceDomain instances interact.

     

    service-domains.png

    ServiceDomain API

     

    The current API for ServiceDomain addresses the core requirements to provide and consume services within SwitchYard.  Instances of ServiceDomain are accessed using static factory methods on ServiceDomains - this is simply a convenience hook for the time being.

     

    public class ServiceDomains {
    
        public synchronized static ServiceDomain getDomain() {
              ...
        }
        
        public synchronized static ServiceDomain createDomain(String name) {
              ...
        }
        
        public static ServiceDomain getDomain(String domainName) {
            ...
        }
        
        public static Set<String> getDomainNames() {
            ...
        }
    }
    

     

    Client code is free to create ServiceDomain instances, although it's likely that this will happen automagically through deployment and/or runtime  configuration once we flesh those pieces out.  Missing from the current ServiceDomains interface is the ability to set configuration and policy details for a ServiceDomain.

     

    In order to provide a service within a ServiceDomain,  the service must be registered.  A given service QName can be registered multiple times, permitting multiple providers for a given service.  Examples of when multiple providers of a service might exist include : a SwitchYard cluster, alternate implementations of a service, two or more versions of the same service, etc.

     

    // get a reference to the default domain
    ServiceDomain domain = ServiceDomains.getDomain();
    // register a service
    Service service = domain.registerService(new QName("http://example.com", "MyService"), myExchangeHandler);
    // when it comes time to stop providing a service (undeploy, shutdown, etc.), it needs to be unregistered
    service.unregister();
    

     

    The handler registered with the service is added as the last handler in the service's HandlerChain.  At present, there is no way for a client to directly manipulate the HandlerChain.  That said, HandlerChain extends ExchangeHandler, which means that a HandlerChain can be registered with a service.  If a client wants to register multiple handlers with a service it can create a HandlerChain instance, add the handlers to that instance, and then register the HandlerChain instance with the service.  The client-supplied handler chain will be added as the last "handler" in the service's HandlerChain.

     

    // build the chain
    HandlerChain chain = new DefaultHandlerChain();
    chain.addFirst("interesting filter", myFilterHandler);
    chain.addFirst("provider code", myServiceHandler);
    
    // register the service with the chain
    Service service = domain.registerService(new QName("http://example.com", "MyService"), chain);
    
    
    
    

     

    To consume a service, clients create an exchange using the ServiceDomain API.  There are two flavors of createExchange() methds - one which takes an ExchangeHandler and one that does not.  A client provides an ExchangeHandler when it wishes to handle reply messages as part of an exchange.  If a  handler does not provide a handler, the runtime will provide a suitable  default that allows tracking unhandled responses (similar to a DLQ).  As  with service registration, a HandlerChain can be used in place of an  ExchangeHandler if multiple handlers are required.

     

    Exchange inOut = domain.createExchange(new QName("http://example.com", "MyService"), ExchangePattern.IN_OUT, myReplyHandler);
    

     

    ServiceDomain SPI

     

    The current SPI is basic and very immature at this stage.  The interfaces are certain to change along with additional interfaces as we begin to fill out the implementation.

     

    EndpointProvider

     

    An endpoint represents the runtime barrier between a service consumer and  provider.  When a provider registers a service, an internal endpoint is created which will serve as the destination for any exchanges created to invoke that service.  API clients have no knowledge of endpoints, it is  strictly an internal detail of the runtime and SPI.  An implementation of EndpointProvider has two responsibilities:

    • Create Endpoint instances that can be used as a delivery endpoint
    • Dispatch exchanges that are sent to an endpoint to the appropriate HandlerChain

     

    public interface EndpointProvider {
        Endpoint createEndpoint(HandlerChain handlerChain);
    }

     

    public interface Endpoint {
        void send(Exchange exchange);
    }

     

    When a ServiceDomain is created, it is bound to an EndpointProvider.  There will be multiple implementations of EndpointProvider based on the delivery requirements of a ServiceDomain.   Individual implementations may have different characteristics in terms of transaction support, persistence, reliability guarantees, etc.   EndpointProvider instances will also need to accept configuration for things like the number of threads to use for dispatching exchanges.

     

     

     

    Interfaces

     

     

    public interface ServiceDomain {
        String getName();
        
        Exchange createExchange(QName service, ExchangePattern pattern);
        Exchange createExchange(QName service, ExchangePattern pattern, ExchangeHandler handler);
        
        Service registerService(QName serviceName, ExchangeHandler handler);
    }

     

    public interface Service {
        QName getName();
        void unregister();
    }