Pixel Core Tutorial – Service Layer

Service layer is a bit more involved, as it hold following functionality

  • Secure methods by assigning permissions per method
  • Define default transaction demarcation on service methods
  • Export methods via DWR

In order to create a service, we will define interface (similar to persistence manager)

package com.pixelnation.gaming.engine.service.iface;

import com.pixelnation.common.service.iface.INlsService;
import com.pixelnation.common.service.iface.IService;
import com.pixelnation.gaming.engine.domain.GameDef;

public interface IGameDefService extends IService<GameDef, Integer>, INlsService

This interface extends from two other interfaces IService and INlsService

INlsService is required only for services working with Localizable domain objects. IService on the other hand define many utility methods that simplify implementation of custom services a breeze.

Implementation of GameDefService will take a bit of explanation

public class GameDefService extends GenericService<GameDef, Integer> implements IGameDefService
  private IGameDefManager m_manager;
  private ILanguageManager m_languageManager;

@Service annotation is required by framework to define this class stereotype (see SpringFramework)

@Transactional by default wraps all methods in transaction

@RemoteProxy tells DWR to export this class as JavaScript class

We will autowire two objects – persistence manager and language manager for use further in the code.

If  you are using IDE, it will suggest you to implement many methods specified in extending interfaces. Let’s go over one sample method

@Transactional(readOnly = true)
public Map<String, Object> getEntityMapped(Integer pId, Collection<Mapping> pMappings) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
  return _getEntityMapped(pId, pMappings);

@RemoteMethod tells DWR to export this method as function in JavaScript class

@Secured specify permission which will be guarding access to this method from unauthorized access. In this case it is ROLE_GAME_DEF_R

@Transactional instructs framework to wrap this method in read-only transaction. If any of underlying calls will try to persist anything into the database without explicitly creating a new transaction, this method will roll back with an exception.

And the meat of the method

return _getEntityMapped(pId, pMappings);

This line calls superclass GenericService to retrieve GameDef object and prepare it for exporting to UI over wire.

One of complexities that need to be taken seriously when using Hibernate as underlying persistence layer is object hierarchy. Let see what will happen if we will send raw GameDef object to UI. First it will be automatically serialized by DWR. In this case DWR implementation will traverse over all properties of GameDef object. Everything will be fine for simple properties, like id, created, updatedBy, etc. But when client property will be encountered, DWR will try traversing whole object and serialize it in depth. And after client it will serialize all other properties of the client, like addresses, contacts and so on. In some cases with circular dependencies whole database could be retrieved.

In order to avoid such behavior, Core framework provides an ability to specify what fields and how to retrieve. All *Mapped methods receive collection of mappings as one of the parameters. Each mapping defines one property of the underlying object and how it should be serialized by DWR. Using this technique server will serialize only partially populated object structure. Mapping class is well described in API documentation.

It is highly recommended to call mapped methods from remote clients where retrieved domain objects contain references to complex object tree.

mapping = [
  {name:'id', type:'primitive'},

This snippet of code shows how JavaScript client can create mapped call. Here it will instruct Core Framework to extract an object, where type is extracted as reference with only two simple fields (ignoring other) and collection of NLS objects is filtered and will return one element only. More on this in another blog describing UI design.