(Quick Reference)

8.2 Services - Reference Documentation

Authors: Andres Almiray

Version: 1.2.0

8.2 Services

Services are responsible for the application logic that does not belong to a single controller. They are meant to be treated as singletons, injected to MVC members by following a naming convention. Services are optional artifacts, and as such there is no default folder created for them when a new application is created.

Services must be located inside the griffon-app/services directory with a Service suffix. The create-service command performs this job for you; also adding a unit test for the given service.

Let's say you need to create a Math service, the command to invoke would be

griffon create-service math

This results in the following files being created

  • griffon-app/services/MathService.groovy - the service class.
  • test/unit/MathServiceTests.groovy - service unit test.

A trivial implementation of an addition operation performed by the MathService would look like the following snippet

class MathService {
    def addition(a, b) { a + b }
}

Using this service from a Controller is a straight forward task. As mentioned earlier services will be injected by name, which means you only need to define a property whose name matches the uncapitalized service name, for example

class MyController {
    def mathService

def action = { model.result = mathService.addition model.a, model.b } }

The type of the service class is optional as basic injection is done by name alone.

Service injection is trivial, it does not provide a full blown DI, in other words further service dependencies will not be resolved. You will need a richer DI solution in order to achieve this, fortunately there are Spring and Weld plugins that do this and more.

Given that services are inherently treated as singletons they are also automatically registered as application event listeners. Be aware that services will be instantiated lazily which means that some events might not reach a particular service if it has not been instantiated by the framework by the time of event publication. It's also discouraged to use the @Singleton annotation on a Service class as it will cause trouble with the automatic singleton management Griffon has in place.

Lastly, all services instances will become available through an instance of type griffon.core.ServiceManager. This helper class exposes available services via a Map. You can query all currently available services in the following manner

app.serviceManager.services.each { name, instance ->
   // do something cool with services
}

You can also query for a particular service instance in the following way

def fooService = app.serviceManager.findService('foo')

It's worth mentioning that the previous method will instantiate the service if it wasn't available up to that point.

All services are instantiated lazily by default. You can change this behavior by adding a configuration flag to Config.groovy

griffon.services.eager.instantiation = true

8.2.1 Service Lifecycle

Services participate in a lifecycle as they are automatically managed by the application. The GriffonService interface defines a pair of methods that every service may override.

public interface GriffonService extends GriffonArtifact {
    void serviceInit();
    void serviceDestroy();
}

The first one is called immediately after the service has been instantiated by the ServiceManager. This is the right place to put initialization code. The app instance should be already set on the service instance, giving you direct access to the application's configuration and i18n facilities.

The second method is also called by ServiceManager when the application is shutting down. Be aware that this method will be called before MVC groups are destroyed.

8.2.2 Service Configuration DSL

Services may have some of its properties defined in external configuration files, for example in Config.groovy, using a simple DSL. Take for example the following service class

class NetworkService {
    String host
    int port
    private Server server

void connect() { if (!server) { server = new Server(host, port) } } }

This service declares 2 public properties (host and port) but does not define any values for them. Inside Config.groovy we find the following definitions

services {
    network {
        host = 'http://acme.com'
        port = 1234
    }
}

The rules are simple to understand given this example.

  • The entry point is the top services section.
  • Each child node identifies the target service by name. Notice that the Service suffix is omitted.
  • Each property within a service block will be set on the service instance.