Dev/guice

From CubeiaWiki

Jump to: navigation, search

Contents

Firebase Guice Support

Firebase works well with Guice and an add-on is released with Guice support started. It contains base-classes for all Firebase artifacts which sets up the injection contexts properly. This page outlines how the guice support add-on works.

Requirements

A basic understanding of Cubeia Firebase is needed to understand this page.

Building

With Maven

To build with Maven you will need the following dependency and repository:

<dependency>
  <groupId>com.cubeia.firebase</groupId>
  <artifactId>guice-support</artifactId>
  <version>1.4</version>
</dependency>
<repository>
  <id>cubeia-nexus</id>
  <url>http://m2.cubeia.com/nexus/content/groups/public/</url>
  <releases>
    <enabled>true</enabled>
  </releases>
  <snapshots>
    <enabled>true</enabled>
  </snapshots>
</repository>

Without Maven

You can easily use the Guice add-on without Maven, just download the following file and add to you build class path:

http://m2.cubeia.com/nexus/content/groups/public/com/cubeia/firebase/guice-support/1.4/guice-support-1.4.jar

Overview

Each artifact in Firebase, service, game and tournament, has a base class in the Guice support JAR. These base classes uses programmatic configuration through abstract methods to set-up an initial injection context. However, they differ in one important way:

  • The game will setup one injection context for each executed event. This context will include the event itself, the table it is executed on and the current state associated with the table. In other words, the injection will be done in a custom event scope.

Guice Game

Quick How-To

Extend GuiceGame:

public class MyGame extends GuiceGame [...]

However, keep in mind...

 Note:  If you override init() in your game implementation, then you need to call super.init() or Guice will not be initialized.

Override 'getConfigurationHelp' to return a configuraton, with your processor class, and optionally your interceptor and listener classes:

public Configuration getConfigurationHelp() {
    return new Configuration() {

	public Class<? extends GameProcessor> getGameProcessorClass() {
            // Return the class of your processor, mandatory
        }
	
	public Class<? extends TableListener> getTableListenerClass() {
            // Return the class of your listener, or null
        }
	
	public Class<? extends TableInterceptor> getTableInterceptorClass() {
            // Return the class of your interceptor, or null
        }
	
	public Class<?> getGameStateClass() {
            // Return the class of your state object, or null
        }
    };
}

Also, if you want to register your own modules, you can override 'preInjectorCreation':

public void preInjectorCreation(List<Module> list) {
    list.add(new MyGameModule());
}

If you need to access bound resources you can override 'postInjectorCreation':

public void postInjectorCreation(Injector injector) {
    // do something smart here...
}

Scope

All injections in the game will be scoped to the currently executing event. Any additional bindings for a game should be done in the Firebase 'event' scope:

bind(MyContract.class).to(MyImpl.class).in(EventScoped.class);

You can register listeners for scope entry and exit events. This can be used to bind additional resources such as transaction boundaries to the thread context, you do this by adding a scope listener to the super-class. For exampel, to add a listener which is managed by Guice you'd do it in the post injector creation, list so:

@Override
public void postInjectorCreation(Injector injector) {
   MyListener l = injector.getInstance(MyListener.class);
   addScopeListener(l);
}

Scope listeners are notified immediately after scope entry and before scope exit.

Special Injections

The following objects are available for ordinary in a game, all in the event scope:

GameProcessor
GameContext
TableGameState
TableMetaData
GameNotifier
TablePlayerSet
TableScheduler
TournamentNotifier
TableWatcherSet
Table

In addition, if you have configured a listener or interceptor or state class via the configuration, these will be injectable as wrll.

Any Firebase service can be injected (must be a service contract), using the special Service injection annotation:

@Service
private DatasourceServiceContract dsContract;  

You can also inject a Log4j logger anywhere using the special Log4j injection annotation:

@Log4j
private Logger log;

Table and Player ID

  NB:  Available from Guice Support 1.3.0.

It is possible to inject the table ID separately, like so:

@Inject
@Named("tableId")
private int tableId;

For all methods originating from a remote player it is also possible to inject the player ID:

@Inject
@Named("playerId")
private int playerId;

However, it is worth noting that some invocations are not originating from any particular player, and for these instances the inserted player ID will be -1:

TournamentProcessor.startRound(Table)
TournamentProcessor.stopRound(Table)
GameProcessor.handle(GameObjectAction, Table)

Guice Service

 NB:  Tis is for Guice Support 1.4+, for older implementations see: Guice Service < 1.4.

Creating a service using Guice is no different in implementation than normally. As usual, these are the first steps:

  • Create a Contract interface.
  • Create an implementation that extends the Contract and Service, but also ma

After this there is a difference, instead of configuring Firebase to use the implementation from #2 above, where going to go the Guice route. So:

  • Make an extension of "GuiceServiceHandler" configuring you contract and implementation classes.
  • Instead of a "service" element in the service descriptor, add a "invocation-handler" element and point it to the class name of your extension from step #3.

Quick How-To

Create an Contract interface for your service:

public interface EchoService extends Contract {

   public String echo(String msg);

}

Then create an implementation that also implement Service:

@Singleton
public class ServiceImpl implements Service, ExchoService {

    @Override
    public String echo(String msg) {
       return msg;
    }

    @Override
    public void init(ServiceContext con) throws SystemException { }

    @Override
    public void start() { }

    @Override
    public void stop() { }

    @Override
    public void destroy() { }

}

Note the "@Singleton" annotation. Your service will be mounted as a singleton automatically, but marking it explicitly might be a good idea anyway.

Now create a handler for your service, this is where the Guice magic will take place:

public class Handler extends GuiceServiceHandler {

    @Override
    protected Configuration getConfiguration() {
        // here we're telling Guice what classes to use
        return new Configuration() {

            @Override
            public ContractsConfig getServiceContract() {
                // implementation class and contract
                return new ContractsConfig(ServiceImpl.class, EchoService.class);
            }
        };
    }
} 

If you want to register your own modules, you can override 'preInjectorCreation':

public void preInjectorCreation(ServiceContext context, List<Module> modules) {
    list.add(new MyServiceModule());
}

Almost there, now open the "service.xml" and remove the "service" element and add this instead (with the right class name of your handler created above):

<invocation-handler>test.Handler</invocation-handler>

Done!

Special Injections

Any Firebase service can be injected (must be a service contract), via the special Service' injection annotation:

@Service
private DatasourceServiceContract dsContract;  

If a service is injected into another service during the initiation phase of the service life-cycle, the service must be proxied, otherwise the injection may result in a null value as the injected service may not be known:

@Service(proxy=true)
private DatasourceServiceContract dsContract;  

You can also inject a Log4j logger anywhere using the special Log4j injection annotation:

@Log4j
private Logger log;

Guice Tournament

Quick How-To

Extend GuiceTournament:

public class MyMTT extends GuiceTournament [...]

Override 'getConfigurationHelp' to return a configuraton, with your actual tournament handler class:

public Configuration getConfigurationHelp() {
    return new Configuration() {

	public Class<? extends TournamentHandler> getTournamentHandlerClass() {
            // Return the class of your implementation
        }
    };
}

Also, if you want to register your own modules, you can override 'preInjectorCreation':

public void preInjectorCreation(List<Module> list) {
    list.add(new MyMttModule());
}

Special Injections

To use the support methods from MTTSupport in your handler, there's an assistant interface you can inject:

@Inject
private TournamentAssist mttAssist;  

You can also inject a Log4j logger anywhere using the special Log4j injection annotation:

@Log4j
private Logger log;

NB: Contrary to the special bindings in Games and Services there is no @Service binding available in the tounament support.

Personal tools