Grails - Services

Services

Grails Services

A service is a class that holds one or more methods that implement business logic. Logical parts of the business logic are contained in separate service classes. Services are demarcated by transactions to make units of work atomic on the persistence level. When certain types of exceptions are thrown transactions are rolled back.

Services need to be registered with the middle tier and also need transactional configuration next to the configuration of optional AOP services.

Services can be registered within a number of different scopes, but the default is application (or singleton) scope therefore your code needs to be thread-safe.

Coding conventions for services

Service class names should start with the name of the service and end with "Service". A country service would thus be called "CountryService".

By default transaction demarcation is enabled for all services, transaction demarcation can be disabled for specific services by creating a "transactional" property and setting it to false:

class CountryService {
    static transactional = false
}

You may also set this property to true in case the default changes in the future, or simply to make it clear that the service is intentionally transactional.

Warning: dependency injection is the only way that declarative transactions work. You will not get a transactional service if you use the new operator such as new BookService()

This results in each method of a service getting wrapped in a transaction. The transaction attribute is set to PROPAGATION_REQUIRED for all service methods. This is hard coded into the Service Plugin. PROPAGATION_REQUIRED, as stated in Transaction Manager terms, will use the current transaction if available or create a new one is one is not available.

Services and scopes

By default access to service methods is not synchronised, so nothing prevents concurrent execution of those functions. In fact, because by default the service is a singleton and may be used concurrently, you should be very careful about storing state in a service. Or take the easy (and better) road and never store state in a service.

Since Grails 0.6 you can change this behaviour by placing a service in a particular scope. The supported scopes are:

  • prototype - A new service is created every time it is injected into another class
  • request - A new service will be created per request
  • flash - A new service will be created for the current and next request only
  • flow - In web flows the service will exist for the scope of the flow
  • conversation - In web flows the service will exist for the scope of the conversation. ie a root flow and its sub flows
  • session - A service is created for the scope of a user session
  • singleton (default) - Only one instance of the service ever exists

Note that if your service is flash, flow or conversation scoped (and if you use http session clustering then the session scope too) it will need to implement java.io.Serializable.
To enable one of the scopes add a static scope property to your class whose value is one of the above:
static scope = "flow"

Dependency Injection

Other services can be injected into a service by creating corresponding properties. To inject the CountryService create a "countryService" property.

def countryService



Earlier versions of Grails supported statically typed injection of services, e.g. {{CountryService countryService}}. Typed injection has an adverse effect on reloading and Grails no longer supports this type of injection.
A javax.sql.DataSource instance can be retrieved for the configured database by adding the property:
javax.sql.DataSource dataSource

This could be used in conjunction with Spring's JdbcTemplate to perform direct transactional SQL operations or with Groovy Sql



Initialization

Rather than implementing a constructor for your service class, it is usually more useful to implement the Spring InitializingBean interface so that injected dependencies are available.

import org.springframework.beans.factory.InitializingBean

class ExampleService implements InitializingBean { def grailsApplication def setting

void afterPropertiesSet() { this.setting = grailsApplication.config.setting } }

The above could not have been done with a constructor as the {{grailsApplication}} instance is not available at that time.

Accessing Services

From a controller

In a typical multitier scenario you can easily access services using dependency injection, for example from a controller:

class CountryService {
    def String sayHello(String name) {
        return "hello ${name}"
    }
}

class GreetingController { def countryService def helloAction = { render(countryService.sayHello(params.name)) } }


That's all, you can test it by typing this URL http://myserver:8080/myapp/greeting/helloAction?name=Falken in your browser location bar. A page containing "hello Falken" will be returned. See Grails controllers for more details about controllers, e.g. changing to access services by type rather than by name.



From a Domain Class

Services can be accessed from within a domain class by adding the Service as a property and declaring the service to be transient.

// Domain class
class MailAction {
   String recipient
   ...

def mailService

static transients = ['mailService']

void execute() { def mail = .... mailService.send(mail) } }






From a servlet or other non-artifact class in src/groovy or src/java:

Alternatively, you can access services from other places of your web application if you need it. Service objects are beans which can be retrieved from the Spring Bean Factory. You can proceed this way:

Implement a java interface containing the service method definitions which you want to be available, for example:

package serviceinterfaces;

public interface CountryServiceInt { public String sayHello(String name); }


Save it in your "<..>/myapp/src/java/serviceinterfaces" directory Modify the CountryService groovy class for implementing the CountryServiceInt interface:
class CountryService implements serviceinterfaces.CountryServiceInt {

def String sayHello(String name) { return "hello ${name}" } }


For accessing the service from a servlet you can do this:
import org.codehaus.groovy.grails.commons.ApplicationHolder;
…
ApplicationContext ctx = (ApplicationContext)ApplicationHolder.getApplication().getMainContext();
CountryServiceInt service = (CountryServiceInt) ctx.getBean("countryService");
String str = service.sayHello(request.getParameter.("name"));
//do something with str

From BootStrap

For accessing the service from within BootStrap.init() you can use dependency injection:

…
    def countryService

def init = { servletContext -> String str = countryService.sayHello(", Starting Application") //do something with str … }