If we talk about services, we must clearly understand what a service is (in our context). In an end-to-end communication arrangement one communication partner provides certain operations that are bundled in a service; a client calls those operations. The managed part of a service refers to the service framework's capabilities.
For example, a service connection could be established by taking certain objects and arranging (marshaling) them with a created set of call parameters, and sending them via technical connection. You also need to consider logging and monitoring, as well as the addition of structures to assist with call configuration and its assignment to the environment in which you want to run the service. Many of these activities are handled by the "Managed Service Framework".
Services are usually described by interfaces (generally speaking, these can be but not necessarily refer to Java interfaces). That said, we should not assume that every interface is a service.
For a service call situation we assume:
This glossary describes the terms used here ( typewriter
style denotes terms that directly correspond with class/interface names):
Term | Description |
---|---|
Adapter | If you think of a stub/skeleton architecture where the skeleton is the service, then the adapter is the stub that communicates with the service. |
Adapter Interface | A Java interface that is not a marker interface and commonly not a pojo interface. |
Assignment | An assignment is the same as the |
Configuration | Mostly, configuration refers to the term "dynamic configuration". Note Make sure that you do not confuse configuration with |
Dynamic Configuration | The term "dynamic configuration" refers to configurations that have specific parameters. For example, an HTTP request has URLs, timeouts and the like, while a mail sender may have SMTP server, security values, ports and so on. The managed service framework does not limit what parameters a service might support, but it offers easy-to-use parameter definitions to handle those configurations. Since the parameters vary from one to another ServiceDefinition, it is called "dynamic". |
Event | A state change triggers an event. Events in this documentation are lifecycle updates on a |
Hook | A hook is the method that will be triggered by an event. Usually they implement |
Intershop platform | The Intershop server software as distributed. |
New Services | New services are implementations that follow the design described in this document. The designation "new" merely serves to contrast to old services. |
Old Services | Enfinity platform versions from 6.4 have service implementations that follow a distinct design. Those services are supported by the new service framework design, with one major limitation: it is not possible to create more than one |
Service | In an end-to-end communication, one communication partner provides some operations that are bundled in a service, while a client calls those operations. |
|
|
| A |
| A |
| A ServiceDefinition is a stateless object that
|
| The |
| The |
| When registering a |
| A |
| The ServiceProvider is a stateful object (as it may hold a ServiceConfigurationBO reference) that serves an adapter that corresponds to the requested adapter interface. It is served by a |
Service type | Synonym for ServiceDefinition, but focuses more on the business view. |
XSD | XML schema description |
This chapter describes some artifacts in the INTERSHOP platform that constitute the Managed Service Framework.
The following diagram shows - extremely simplified - the parts of the managed service framework.
ServiceDefinition
that refers to one or more adapters and supports access to them.ServiceConfigurationBORepository
that is the link to the context whereby services are accessed and these may own specific settings for a service.ServiceConfigurationBO
that associates a ServiceConfigurationBORepository
with the ServiceDefinition
. Thus, ServiceConfigurationBORepository
specific settings are stored and so the adapters operate ServiceConfigurationBORepository
-aware, hence App-aware (if ServiceConfigurationBORepository
is an App-Repository).The following chapters will take a closer look at the artifacts specified here and introduce additional artifacts that surround those previously mentioned.
While an adapter is the object that makes the service call possible, still, it is not a part of the Managed Service Framework. The adapter is a component that "knows" how to access the service. In the INTERSHOP platform, an adapter is defined by any Java interface (see the following chapter). There is no restriction on the adapter implementation. Essentially, any object can become a service adapter as long as it implements at least one interface.
The adapter has to take care of:
As mentioned before, any object that implements a Java interface can serve as an adapter.
However, there are some constraints that must be considered: Avoid adapters for marker interfaces and pojos.
Example:
Note
You could think of the Address
interface as being an adapter interface. However, this would mean that getting a name or getting a street are service calls. That would not be very useful.
In the INTERSHOP platform, the adapter interface is represented in two shapes:
<T>
in the getServiceAdapter(Class<T>)
method of the ServiceProvider
in bc_service andgetServiceAdapter(Class<T>)
method and in the return value of the getServiceInterfaces()
method of the ServiceDefinition
As a programmer you might be required to use an existing service, to implement a new stub or to do both.
After reading this concept and with the Cookbook - Managed Service Framework at hand, you should be able to fulfill your task.
Note
If you are asked to enhance/change the framework itself, do not forget to update this documentation.
Note
Adapter interfaces should be marked as managed service using com.intershop.component.service.capi.service.ManagedService
. This marker annotation is used by external tools.
This is how a service is looked up and how it is called ("use an existing service") - using the "Address" example mentioned above:
ServiceConfigurationBORepository serviceConfigurationBORepository = applicationBO.getRepository( "ServiceConfigurationBORepository" ); for (AddressValidator addressValidator : serviceConfigurationBORepository.getServiceAdapters( AddressValidator.class )) { addressValidator.validate( address ); }
The ServiceConfigurationBORepository
will be discussed later in this document. The AddressValidator
is a Java interface that defines a stub for an address validator service. The variable addressValidator
contains an adapter implementation. The method validate
encapsulates the address validation operation of a validation service. The address
parameter is considered an Address
object as defined in the example above.
To implement a new stub for a service, follow these proceedings:
To make the new service adapter available in the INTERSHOP platform, some more artifacts have to be established, which will be discussed in the following chapters:
Optionally, these parts may also be needed:
There are abstract adapter classes that provide a lot of functionality that may be needed: SingleOperationAdapter
and MultiOperationAdapter
. For more details refer to the Cookbook - Managed Service Framework.
A ServiceDefinition
is a stateless object that
ServiceConfigurationBO
state changes by defining lifecycle hooksSometimes several adapter interfaces can be bundled for functional reasons. Think, for example, of an inventory service. There might be an interface that just retrieves the quantity on stock and another one that manages a reservation lifecycle. Thus, there might be a QuantityRequest
interface and a Reservation
interface. The two of them could be bundled to one InventoryServiceDefinition
that supports both interfaces. The supported interfaces are retrievable by accessing the Collection<Class<?>> getServiceInterfaces()
method.
When a service is looked up using the ServiceConfigurationBORepository
, the lookup strategy of the framework will ask the ServiceDefinition
to serve an appropriate ServiceProvider
for a given ServiceConfigurationBO
. That means, the ServiceDefinition
must analyze the ServiceConfigurationBO
and select a suitable ServiceProvider
based on it.
The ServiceDefinition
might cache instances, connections o.t.l. To remain up-to-date consistently, the ServiceDefinition
can implement BusinessObjectListener<ServiceConfigurationBO>
.
The ServiceProvider
is a stateful object (as it may hold a ServiceConfigurationBO
reference) that serves an adapter that corresponds to the requested adapter interface.
Thus, it does not define more than this method:
<T> T getServiceAdapter( Class<T> serviceInterface );
As both ServiceDefinition
and ServiceProvider
implementations are located in the internal section of a cartridge and the ServiceDefinition
provides a ServiceProvider
according to a ServiceConfigurationBO
, there must be a technique to generically switch the ServiceProvider
, if it is intended to be able to add new adapters in new cartridges that support a certain ServiceDefinition
. The AbstractServiceDefinition
offers capabilities to add adapters from outside the original cartridge.
The ServiceDefinitionRegistry
is the central point where ServiceDefinition
instances are registered and retrieved. Usually, ServiceDefinition
instances are wired to the ServiceDefinitionRegistry
using the Component Framework.
The entry of the ServiceDefinitionRegistry
contains some more (descriptive) attributes in addition to the ServiceDefinition
itself.
There are so-called "old" services (services that have been implemented prior to INTERSHOP 7) and "new" services.
ServiceInformation
implementations,ServiceDefinition
implementations. The old services will be supported.The ServiceInformation
interfaces provide some methods that correspond with some component configuration in the new framework. The ServiceInformationServiceDefinitionBridge
is a wrapper around a ServiceInformation
and turns it to a ServiceDefinition
.
Component configuration key (new) |
| prefix | |
---|---|---|---|
| does not exist for old services. | constant | A |
| getGroupID() | service.group.name | All whitespace will be replaced by underscores ( |
| getParameterGroupID() | not prefixed | |
| getServiceID() | not prefixed | A |
| does not exist for old services |
| All whitespaces will be replaced by underscores ( |
The ServiceDefinitionRegistry
also holds references to so-called "chain elements". These are handlers that are part of the service call chain and may listen to the data exchange stream for, e.g., logging or monitoring reasons.
The following diagram shows the references between the adapter, adapter interface, ServiceProvider
, ServiceDefinition
, ServiceDefinitionRegistry.Entry
and ServiceDefinitionRegistry
.
There is an AbstractServiceDefinition
that offers lots of implementations and makes it straightforward to have a new ServiceDefinition
implementation (incl. a sufficient ServiceProvider
). For more details refer to the Cookbook - Managed Service Framework.
The ServiceDefinitionKeyPO
represents the link between the "Java world" and the persistent layer. A ServiceDefinition
is uniquely referenced by the cartridge id and the service definition id. This is because a programmer just needs to ensure that the ServiceDefinition
has a unique service definition id within "his/her" cartridge. Some database objects, like ServicePermissionPO
and ServiceConfigurationPO
are holding references to that ServiceDefinitionKeyPO
. These references are used by the ServiceConfigurationBORepository
and ServiceConfigurationBO
to be able to lookup "their" ServiceDefinition
objects.
A ServiceConfigurationBORepository
is a central point to manage the lifecycle of service configurations and it supports access to the adapters. Different ServiceConfigurationBORepository
objects are available for different contexts, such as applications (see Concepts - App and Application Framework (until 6.6.x)). So a ServiceConfigurationBO
associates a ServiceDefinition
with a ServiceConfigurationBORepository
. Any artifact that wants to handle services in any way must have a ServiceConfigurationBORepository
to do so. When it is intended to use a service, it should be checked first whether there is a ServiceConfigurationBORepository
for the context in mind.
The diagram above shows how ServiceConfigurationBORepository
, ServiceConfigurationBO
and ServiceDefinition
correspond with each other.
It illustrates
ServiceConfigurationBORepository
can have a ServiceMasterRepository
- a kind of parent - that may share its ServiceConfigurationBO
objects to the actual ServiceConfigurationBORepository
ServiceConfigurationBORepository
can have one or more dependents - a kind of child element - that may get ServiceConfigurationBO
objects shared from the actual ServiceConfigurationBORepository
ServiceConfigurationBORepository
can own and refer to a ServiceConfigurationBO
or just refer to a ServiceConfigurationBO
ServiceConfigurationBO
means that the actual ServiceConfigurationBORepository
controls the lifecycle of it, i.e., it creates it. You can say "the ServiceConfigurationBO
is located in the ServiceConfigurationBORepository
". If a ServiceConfigurationBORepository
owns a ServiceConfigurationBO
, none of its parents can refer to it (because that would mean there is a circle dependency in the ServiceConfigurationBORepository
structure).ServiceConfigurationBO
means that it is a shared one from a ServiceMasterRepository
. All "children" and "grandchildren" a.s.o. may refer to ServiceConfigurationBO
objects. They may also control the activation behavior for themselves.ServiceDefinition
can have 0..n
ServiceConfigurationBO
that refers to it. Thus, several configurations for different use cases can be managed.The ServiceConfigurationBORepository
is just a composition of three interfaces that define three aspects of it:
ServiceExecutable
that covers methods needed to access a serviceServiceInstantiable
that covers methods to configure and to control the lifecycle of a ServiceConfigurationBO
ServiceMasterRepository
that offers methods to access the ServiceConfigurationBORepository
hierarchy. This is needed to be able to provide sharing capabilities.Service types can be allowed or prohibited for ServiceConfigurationBORepository
instances.
Prohibiting a service type means that
ServiceConfigurationBORepository
As discussed above, a ServiceDefinitionKeyPO
is the object that connects the ServiceDefinition
, which is a pure Java object, with the persistence layer.
From the framework's point of view, all service-related tables can be empty at the beginning. Under normal circumstances they are not, since quite a few services are configured during DBInit.
But since ServicePermissionPO
has a mandatory reference to a ServiceDefinitionKeyPO
and nothing can be done with services before a ServicePermissionPO
is in place, at first a ServiceDefinitionKeyPO
must be in place and is created when allowing (or even prohibiting!) a ServiceDefinition.
A ServiceConfigurationBO
associates a ServiceConfigurationBORepository
with a ServiceDefiniton
. It has a name (and localizable display name and descriptions), can be made "accessible" (that will be discussed later on), and it may refer to some specific settings (called "Dynamic Configuration" within this document).
From the persistence layer point of view, the ServiceConfigurationBO
is an aggregated object that consists of ServiceConfigurationPO
and ServiceAssignmentPO
. The ServiceConfigurationPO
has attributes and methods that are valid for all artifacts that use the ServiceConfigurationBO
object, while a ServiceAssignmentPO
contains attributes and values that re-define the activation status of a service configuration. Those values are:
ServiceConfigurationBORepository
ServiceConfigurationBORepository
) and if so, whether it should be activated by default or not or whether the usage is even mandatoryThere are different requirements with regard to when a service can be executed from a certain context:
Note
Keep in mind that locally activated can be set explicitly, but also implicitly via the sharing rule of a master repository. That is, if there is no explicit local activation, the master repositories are then looked up. Their sharing rules will control its activation state in that case:
| default activation state for descending repositories |
---|---|
UNSHARED | not activated |
SHARED_ACTIVATED | activated |
SHARED_DEACTIVATED | not activated |
MANDATORY | activated - overrides locally activated flags |
"Dynamic configurations" are configurations that are not "hard" bound to the ServiceConfigurationBO
object, but the ServiceConfigurationBO
is rather a key to the appropriate configuration definition and values.
Some services might not need any specific configurations, others do. Configuration parameters will differ widely. Thus, configuration parameter definitions must be configurable.
The usage of ParameterGroup
definitions is supported by the managed service framework, but it is not required to do so. There are no methods defined in the ServiceConfigurationBO
to access any dynamic configurations.
To access the dynamic configurations, commonly extensions of the ServiceConfigurationBO
should be used. There is an ORMServiceConfigurationBOConfigurationExtensionImpl
, for example. It implements a ConfigurationProvider
interface whose only method is getConfiguration(): Configuration
. See Concept - Component Framework (valid to 7.4 CI) to learn more about the Configuration
artifacts.
To make a dynamic configuration editable, the ParameterGroup
approach should be considered.
The following steps are needed to establish a ParameterGroup
together with the ability to edit it in the back office:
ParameterGroup
ID when registering a ServiceDefinition.
Following this approach there are parameters in the Intershop Commerce Management that must be defined. For detailed instructions, see the Cookbook - Managed Service Framework.
The following diagram is a "big picture" of the discussed artifacts, but shows more detail than in the introduction of this chapter.
From the ServiceExecutable
to the adapter interface, there is an imaginary "horizontal line" of artifacts that are obviously used during a service call, and the blue artifact section shows the persistent artifacts. All others are surrounding classes that help to manage the core artifacts.
In the chapter "Adapter Interfaces", it has already been discussed how a service can be called. The following diagram repeats the steps once more:
The red section in the diagram above is worth a closer look:
As mentioned above, there are 4 persistent objects handled by the managed service framework:
Persistent Object | Table name | Extensible/AV |
---|---|---|
|
| |
|
| |
|
| |
|
|
Without any database initialization none of the services will be available.
Sometimes specific services might be crucial to make the shop work properly, for example services that are used during the checkout. Therefore, these services should be available "out-of-the-box", i.e., after a server installation.
Therefore, 3 preparers are available to initialize a configuration:
ServiceAssignmentPreparer
ServiceConfigurationPreparer
ServicePermissionPreparer
There is no specific preparer to add rows to the ServiceDefinitionKey
table since an entry is implicitly added "on the fly". That is, a ServicePermission
or a ServiceConfiguration
has a mandatory foreign key that refers to a ServiceDefinitionKey
. When one of them is to be created, the ServiceDefinitionKey
is looked up and if it is not already in place, it is created and used.
There is no way to add configuration parameters in DBInit. If that is required, a preparer must be implemented that adds values to the ServiceConfiguration-AV table as long as the type is a "new" type ("old" services use domain preferences for their configuration parameters).
The Cookbook - Managed Service Framework includes a chapter that describes in detail how to use the named preparers.
The monitoring of services is realized with MXBeans and handlers. The ServiceChain handles all registered handlers for a service, including MonitorHandler, LogHandler, and CachingHandler.
If the MonitorHandler is registered, it tracks various data, like successful requests, average answer time, failures etc.
A call works like this:
The RequestStatistics requires 3 parameters, which can be adjusted in the configuration tab of a service:
Long Call Threshold | If an answer of a request takes longer than the number defined in "Long Call", the request is considered a longCall |
Number of Entries | The maximum number of entries, that will be saved in the statistics |
Notification Threshold | Percentage of requests that have to be failures or timeouts for the notfication to be sent |
Depending on those parameters, the statistics can return a status which is either ok (green), with errors (yellow), with exceptions (red), or not used (gray).