Skip to content

Instantly share code, notes, and snippets.

@xhanin
Last active August 29, 2015 14:00
Show Gist options
  • Save xhanin/11319869 to your computer and use it in GitHub Desktop.
Save xhanin/11319869 to your computer and use it in GitHub Desktop.
RESTX JDBC support with BoneCP for connexion pool and Flyway for migration
// In AppModule class, or could be in a dedicated module.
@Provides
public DBI dbi(@Named("datasource") DataSource ds) {
return new DBI(ds);
}
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.jolbox.bonecp.BoneCPDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import restx.config.Settings;
import restx.config.SettingsKey;
import restx.factory.AutoStartable;
import restx.factory.Module;
import restx.factory.Provides;
import javax.inject.Named;
import javax.sql.DataSource;
@Module
public class BoneCpModule {
private static final Logger logger = LoggerFactory.getLogger(BoneCpModule.class);
@Settings
public static interface DatasourceSettings {
@SettingsKey(key = "restx.jdbc.driver")
String jdbcDriver();
@SettingsKey(key = "restx.jdbc.url")
String jdbcUrl();
@SettingsKey(key = "restx.jdbc.username")
String jdbcUsername();
@SettingsKey(key = "restx.jdbc.password")
Optional<String> jdbcPassword();
@SettingsKey(key = "restx.jdbc.admin.username")
Optional<String> jdbcAdminUsername();
@SettingsKey(key = "restx.jdbc.admin.password")
Optional<String> jdbcAdminPassword();
}
@Provides
public AutoStartable startDatasources(@Named("datasource") DataSource dataSource,
@Named("adminDatasource") DataSource adminDatasource) {
return new AutoStartable() {
@Override
public void start() {
logger.info("connected to database");
}
};
}
// datasource used for the app, DML grants are enough
@Provides
public DataSource datasource(DatasourceSettings settings) {
try {
Class.forName(settings.jdbcDriver());
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("invalid driver " + settings.jdbcDriver() + ": it can't be loaded." +
" Check your classpath and settings.");
}
BoneCPDataSource ds = new BoneCPDataSource();
ds.setJdbcUrl(settings.jdbcUrl());
ds.setUsername(settings.jdbcUsername());
ds.setPassword(settings.jdbcPassword().or(""));
return ds;
}
// datasource used to migrate the schema, need DDL grants
@Provides
public DataSource adminDatasource(DatasourceSettings settings) {
try {
Class.forName(settings.jdbcDriver());
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("invalid driver " + settings.jdbcDriver() + ": it can't be loaded." +
" Check your classpath and settings.");
}
BoneCPDataSource ds = new BoneCPDataSource();
ds.setJdbcUrl(settings.jdbcUrl());
ds.setUsername(settings.jdbcAdminUsername().or(settings.jdbcUsername() + "-admin"));
ds.setPassword(settings.jdbcAdminPassword().or(settings.jdbcPassword()).or(""));
return ds;
}
}
import org.skife.jdbi.v2.Handle;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Store current DBI context in a thread local
*/
public class DBIContext {
private static final ThreadLocal<Handle> current = new ThreadLocal<>();
public static void set(Handle handle) {
current.set(handle);
}
public static Handle get() {
return checkNotNull(current.get());
}
public static void clear() {
current.remove();
}
}
import com.google.common.base.Optional;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import restx.*;
import restx.factory.Component;
import java.io.IOException;
/**
* Auto Open DBI Handle for each request, setting the current Handle in DBIContext.
*/
@Component
public class DBIFilter implements RestxFilter, RestxHandler {
private final DBI dbi;
public DBIFilter(DBI dbi) {
this.dbi = dbi;
}
@Override
public Optional<RestxHandlerMatch> match(RestxRequest req) {
if (req.getRestxPath().startsWith("/@")) {
return Optional.absent();
}
return RestxHandlerMatch.of(Optional.of(new StdRestxRequestMatch(req.getRestxPath())), this);
}
@Override
public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx) throws IOException {
try (Handle h = dbi.open()) {
DBIContext.set(h);
ctx.nextHandlerMatch().handle(req, resp, ctx);
} finally {
DBIContext.clear();
}
}
}
import com.googlecode.flyway.core.Flyway;
import restx.AppSettings;
import restx.factory.AutoStartable;
import restx.factory.Component;
import javax.inject.Named;
import javax.sql.DataSource;
@Component
public class FlywayAutoMigrate implements AutoStartable {
private final DataSource dataSource;
private final AppSettings appSettings;
public FlywayAutoMigrate(@Named("adminDatasource") DataSource dataSource,
AppSettings appSettings) {
this.dataSource = dataSource;
this.appSettings = appSettings;
}
@Override
public void start() {
Flyway flyway = new Flyway();
flyway.setDataSource(dataSource);
// set the locations depending on the mode, allow for instance to have more scripts in 'dev', like data initialization
if ("dev".equals(appSettings.mode())) {
flyway.setLocations("db");
}
flyway.migrate();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment