Roughly speaking, a service is a piece of code (Java, Simple-method, Groovy, etc.) that can be run inside of the SCIPIO framework. SCIPIO services can also be “exported” as web services (SOAP, REST, XML-RPC, etc.) using the export=”true” option (attribute of the service element in it’s definition). Services can be chained (i.e. call each others), triggered by ECAs (Event Condition Action) and run asynchronously which gives them a lot of power and allow’s them to be implemented instantly. They always carry the session data in a context variable and can be rolled back if they threw an error (services are transactional with timeout).
Dissecting a Service
A service is composed of 3 parts:
Declaration
Before a service can be used, you need to load it through a declaration file, which will contain your service definition. This is done inside your component configuration file.
<!-- Service definitions --> <service-resource type="model" loader="main" location="servicedef/services.xml"/>
Definition
Services are defined in Service Definition Files, which are essentially XML files that describe the behavior of the service on a meta level. Service definitions consist of a unique name, a specific service engine (read: programming language) and location of the source (either the class or file location, i.e.: component://demosuite/script/com/ilscipio/data/OrderData.groovy). Input and output-fields are defined. Below is an example of a service definition:
<service name="userLogin" engine="java" location="org.ofbiz.commonapp.security.login.LoginServices" invoke="userLogin"> <description>Authenticate a username/password; create a UserLogin object</description> <attribute name="login.username" type="String" mode="IN"/> <attribute name="login.password" type="String" mode="IN"/> <attribute name="userLogin" type="org.ofbiz.entity.GenericValue" mode="OUT" optional="true"/> </service>
For a service definition, the following attributes are available:
Attribute | Description |
---|---|
auth | Does this service require authorization? (true/false) |
debug | Enable verbose debugging when calling this service? |
default-entity-name | The default Entity to use for auto-attributes |
engine | The name of the engine (defined in serviceengine.xml). |
export | Is this service allowed to be accessed via SOAP/HTTP/JMS? (true/false) |
invoke | The method name of the service. |
location | The location or package of the service’s class. |
max-retry | Sets the max number of times this service will retry when failed (persisted async only) |
name | The unique name of the service. |
require-new-transaction | Require a new transaction for this service |
semaphore | Defines how concurrent calls to this service should be handled: none: multiple calls to this service may run concurrently wait: while this service is running, queue any subsequent calls fail: while this service is running, fail any subsequent calls |
semaphore-wait-seconds | When semaphore=”wait” how many seconds to wait before failing the service call |
sempahore-sleep | When semaphore=”wait” how often (in milliseconds) to check if the waiting service call can be run |
transaction-timeout | Override the default transaction timeout, only works if we start the transaction |
use-transaction | Create a transaction for this service (if one is not already in place) |
validate | Do we validate the attributes found below for name and type matching? (true/false) |
The engine element defines the way the service will be implemented. Most commonly, this will be either java or groovy, but others, such as interface, entity-auto and group are available.
Implementation
A typical service implementation accepts the DispatchContext (a Map that contains information about the current service context) and a service context (Map containing the IN-parameters). A result map is expected that contains the status of the service and all required OUT- & INOUT- parameters. Example:
public static Map<String, Object> serviceName(DispatchContext ctx, Map<String, ? extends Object> context) {
Map result = ServiceUtil.returnSuccess(); // creates empty Map with parameter "result" set to SUCCESS
// my Code ..
return resultMap;
}
Inside of the service you can rely on:
- The delegator to access your entities (database)
Delegator delegator = ctx.getDelegator();
- The dispatcher for running services
LocalDispatcher dispatcher = ctx.getDispatcher();