Pregunta

For many clients for things like redis and mongodb, you normally have to create a singleton reference to the client because it is a pool of connections to redis/mongodb.

In a play app, how can I create a static reference that I can just use in all my controller or model logic etc?

¿Fue útil?

Solución 3

You can either create your connection pool/client instance in an Object and access it from your controllers. I personally hate this solution because it makes unit testing controllers very complicated.

Or you can change the way you instantiate your controllers to use GlobalSettings: http://www.playframework.com/documentation/2.2.2/ScalaDependencyInjection

Otros consejos

In my case, I am using slick and MySQL, but I think that what You want to achieve is similar. I also don't know if it is the best solution... I am defining scala object with connection to database, than this conection is wrapped in trait, wich is extended in places where I need a database connection. (In the middle is of course pooling framework).

package models
import scala.slick.driver.MySQLDriver.simple._
import play.api.Play
import play.api.Play.current

import com.mchange.v2.c3p0._;

object Connection {
  val databaseURL = Play.configuration.getString("db.default.url").get
  val databaseUser = Play.configuration.getString("db.default.user").get
  val databasePassword = Play.configuration.getString("db.default.password").get
  val databaseDriver = Play.configuration.getString("db.default.driver").get

  val singletonDatabase = {
    val ds = new ComboPooledDataSource
    ds.setDriverClass(Connection.databaseDriver)
    ds.setJdbcUrl(Connection.databaseURL)
    ds.setUser(Connection.databaseUser)
    ds.setPassword(Connection.databasePassword)
    ds.setMinPoolSize(0);
    ds.setMaxPoolSize(1000);
    ds.setCheckoutTimeout(3000);
    ds.setAcquireIncrement(10);
    ds.setMaxStatements(50);
    ds.setIdleConnectionTestPeriod(60);
    ds.setPreferredTestQuery("SELECT 1;");
    ds.setTestConnectionOnCheckout(false);
    Database.forDataSource(ds)
  }

}
trait DatabaseConnector {

  val database = Connection.singletonDatabase

}

You can create a singleton object by creating it as a Plugin. For my project I had a similar need to build a singleton for RedisPool class. See the complete working code below:

package plugins.redis;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Application;
import play.Configuration;
import play.Plugin;
import plugins.PluginUtils;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisPlugin extends Plugin {

private final Application application;
private JedisPool jedisPool;
private static final Logger LOGGER = LoggerFactory.getLogger("application");
//default constants for Redis
public static final String REDIS_DEFAULT_HOST="localhost";
public static final int REDIS_DEFAULT_PORT=6379;
public static final int REDIS_DEFAULT_TIMESOUT=2000;

public RedisPlugin(Application application){
    this.application = application;
}

public static Integer getIntConfig(Configuration configuration, String configKey, Integer... defaultVal) {
    String val = configuration.getString(configKey);
    if (StringUtils.isBlank(val)) {
        if(defaultVal != null && defaultVal.length>0){
            return defaultVal[0];
        }
        return null;
    } else {
        return NumberUtils.toInt(val);
    }
}

public static Long getLongConfig(Configuration configuration, String configKey, Long... defaultVal) {
    String val = configuration.getString(configKey);
    if (StringUtils.isBlank(val)) {
        if(defaultVal != null && defaultVal.length>0){
            return defaultVal[0];
        }
        return null;
    } else {
        return NumberUtils.toLong(val);
    }
}

public static Boolean getBoolConfig(Configuration configuration, String configKey, Boolean... defaultVal) {
    return BooleanUtils.toBooleanObject(configuration.getString(configKey));
}   

@Override
public void onStart() {
    Configuration configuration = application.configuration();
    String host = getConfig(configuration, "redis.host", REDIS_DEFAULT_HOST);
    if (StringUtils.isBlank(host)) {
        LOGGER.error("Redis host is absent");
        return;
    }
    Integer port = getIntConfig(configuration, "redis.port", REDIS_DEFAULT_PORT);
    if (port == null) {
        LOGGER.error("Redis port is absent");
        return;
    }
    Integer timeout = getIntConfig(configuration, "redis.timeout", REDIS_DEFAULT_TIMESOUT);
    if (timeout == null) {
        LOGGER.error("Redis timeout is absent");
        return;
    }
    String password = getConfig(configuration, "redis.password");
    jedisPool = new JedisPool(getJedisConfig(), host, port, timeout, password);
    super.onStart();
}

public JedisPool getJedisPool() {
    return jedisPool;
}

@Override
public void onStop() {
    jedisPool.destroy();
    super.onStop();
}

@Override
public boolean enabled() {
    return true;
}

public JedisPoolConfig getJedisConfig(){
    JedisPoolConfig poolConfig = new JedisPoolConfig();
    Configuration configuration = application.configuration();
    Integer maxIdle = getIntConfig(configuration, "redis.pool.maxIdle");
    if(maxIdle != null){
        poolConfig.setMaxIdle(maxIdle);
    }
    Integer minIdle = getIntConfig(configuration, "redis.pool.minIdle");
    if(minIdle != null){
        poolConfig.setMinIdle(minIdle);
    }
    Integer numTestsPerEvictionRun = getIntConfig(configuration, "redis.pool.numTestsPerEvictionRun");
    if(numTestsPerEvictionRun != null){
        poolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
    }
    Boolean testOnBorrow = getBoolConfig(configuration, "redis.pool.testOnBorrow");
    if(testOnBorrow != null){
        poolConfig.setTestOnBorrow(testOnBorrow);
    }
    Boolean testOnReturn = getBoolConfig(configuration, "redis.pool.testOnReturn");
    if(testOnReturn != null){
        poolConfig.setTestOnReturn(testOnReturn);
    }
    Boolean testWhileIdle = getBoolConfig(configuration, "redis.pool.testWhileIdle");
    if(testWhileIdle != null){
        poolConfig.setTestWhileIdle(testWhileIdle);
    }
    Boolean lifo = getBoolConfig(configuration, "redis.pool.lifo");
    if(lifo != null){
        poolConfig.setLifo(lifo);
    }
    Long timeBetweenEvictionRunsMillis = getLongConfig(configuration, "redis.pool.timeBetweenEvictionRunsMillis");
    if(timeBetweenEvictionRunsMillis != null){
        poolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
    }
    Long softMinEvictableIdleTimeMillis = getLongConfig(configuration, "redis.pool.softMinEvictableIdleTimeMillis");
    if(softMinEvictableIdleTimeMillis != null){
        poolConfig.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
    }
    Boolean exhaustedAction = getBoolConfig(configuration, "redis.pool.blockWhenExhausted");
    if(exhaustedAction != null){
        poolConfig.setBlockWhenExhausted(exhaustedAction);
    }
    return poolConfig;
}

}

In Play framework version 2.4.x there's now a built in support for singletons and injections.

https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top