package ameba.db.ebean;
import ameba.container.event.ShutdownEvent;
import ameba.core.Application;
import ameba.db.DataSourceManager;
import ameba.db.PersistenceExceptionMapper;
import ameba.db.ebean.internal.ModelInterceptor;
import ameba.db.ebean.jackson.JacksonEbeanModule;
import ameba.db.ebean.jackson.JsonIOExceptionMapper;
import ameba.db.ebean.migration.EbeanMigration;
import ameba.db.migration.Migration;
import ameba.db.migration.models.ScriptInfo;
import ameba.db.model.ModelManager;
import ameba.event.SystemEventBus;
import ameba.i18n.Messages;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.google.common.collect.Lists;
import io.ebean.EbeanServer;
import io.ebean.EbeanServerFactory;
import io.ebean.PersistBatch;
import io.ebean.config.ContainerConfig;
import io.ebean.config.JsonConfig;
import io.ebean.config.PropertiesWrapper;
import io.ebean.config.ServerConfig;
import io.ebeaninternal.api.SpiEbeanServer;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.hk2.utilities.binding.ScopedBindingBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* <p>EbeanFeature class.</p>
*
* @author icode
* @since 2013-08-07
*/
public class EbeanFeature implements Feature {
/**
* Constant <code>SORT_PARAM_NAME="model.query.param.sort"</code>
*/
public static final String SORT_PARAM_NAME = "model.query.param.sort";
/**
* Constant <code>PAGE_PARAM_NAME="model.query.param.page"</code>
*/
public static final String PAGE_PARAM_NAME = "model.query.param.page";
/**
* Constant <code>PER_PAGE_PARAM_NAME="model.query.param.prePage"</code>
*/
public static final String PER_PAGE_PARAM_NAME = "model.query.param.prePage";
/**
* Constant <code>REQ_TOTAL_COUNT_PARAM_NAME="model.query.param.requireTotalCount"</code>
*/
public static final String REQ_TOTAL_COUNT_PARAM_NAME = "model.query.param.requireTotalCount";
/**
* Constant <code>REQ_TOTAL_COUNT_HEADER_NAME="model.query.requireTotalCount.header"</code>
*/
public static final String REQ_TOTAL_COUNT_HEADER_NAME = "model.query.param.requireTotalCount.header";
/**
* Constant <code>DEFAULT_PER_PAGE_PARAM_NAME="model.query.param.perPage.default"</code>
*/
public static final String DEFAULT_PER_PAGE_PARAM_NAME = "model.query.param.perPage.default";
/**
* Constant <code>DEFAULT_PER_PAGE_PARAM_NAME="model.query.param.perPage.max"</code>
*/
public static final String MAX_PER_PAGE_PARAM_NAME = "model.query.param.perPage.max";
/**
* Constant <code>FILTER_PARAM_NAME="model.query.param.filter"</code>
*/
public static final String FILTER_PARAM_NAME = "model.query.param.filter";
private static final Logger logger = LoggerFactory.getLogger(EbeanFeature.class);
private static final List<EbeanServer> servers = Lists.newArrayList();
@Inject
private ServiceLocator locator;
@Context
private ObjectMapper objectMapper;
@Context
private XmlMapper xmlMapper;
@Inject
private Application application;
/**
* {@inheritDoc}
*/
@Override
public boolean configure(final FeatureContext context) {
if (!context.getConfiguration().isRegistered(PersistenceExceptionMapper.class)) {
context.register(PersistenceExceptionMapper.class);
}
if (context.getConfiguration().isRegistered(ModelInterceptor.class)) {
return false;
}
context.register(ModelInterceptor.class)
.register(JsonIOExceptionMapper.class);
final Configuration appConfig = context.getConfiguration();
final JsonFactory jsonFactory = objectMapper.getFactory();
ContainerConfig containerConfig = new ContainerConfig();
Properties cp = new Properties();
cp.putAll(appConfig.getProperties());
containerConfig.loadFromProperties(cp);
for (final String name : DataSourceManager.getDataSourceNames()) {
final ServerConfig config = new ServerConfig() {
@Override
public void loadFromProperties(Properties properties) {
loadSettings(new PropertiesWrapper("db", name, properties));
}
};
config.setName(name);
config.setJsonInclude(JsonConfig.Include.NON_EMPTY);
config.setPersistBatch(PersistBatch.ALL);
config.setUpdateAllPropertiesInBatch(false);
config.setH2ProductionMode(true);
config.loadFromProperties(cp);
config.setPackages(null);
config.setDataSourceJndiName(null);
config.setDataSource(DataSourceManager.getDataSource(name));//设置为druid数据源
config.setJsonFactory(jsonFactory);
config.setContainerConfig(containerConfig);
config.setDisableClasspathSearch(true);
if (name.equals(DataSourceManager.getDefaultDataSourceName())) {
config.setDefaultServer(true);
} else {
config.setDefaultServer(false);
}
config.addClass(ScriptInfo.class);
Set<Class> classes = ModelManager.getModels(name);
if (classes != null) {
classes.forEach(config::addClass);
}
logger.debug(Messages.get("info.db.connect", name));
final EbeanServer server = EbeanServerFactory.create(config);
logger.info(Messages.get("info.db.connected", name, appConfig.getProperty("db." + name + ".url")));
JacksonEbeanModule module = new JacksonEbeanModule(server, locator);
objectMapper.registerModule(module);
xmlMapper.registerModule(module);
servers.add(server);
}
SystemEventBus.subscribe(ShutdownEvent.class, event -> {
servers.forEach(server -> server.shutdown(true, true));
servers.clear();
});
ServiceLocatorUtilities.bind(locator, new AbstractBinder() {
@Override
protected void configure() {
for (EbeanServer server : servers) {
String name = server.getName();
createBuilder(server).named(name);
if (name.equals(DataSourceManager.getDefaultDataSourceName())) {
createBuilder(server);
}
bind(new EbeanMigration(application, (SpiEbeanServer) server))
.to(Migration.class)
.named(name)
.proxy(false);
}
}
private ScopedBindingBuilder<SpiEbeanServer> createBuilder(EbeanServer server) {
return bind((SpiEbeanServer) server)
.to(SpiEbeanServer.class)
.to(EbeanServer.class)
.proxy(false);
}
});
return true;
}
}