Skip to content

Commit

Permalink
Move cleaning support into Agroal
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Mar 18, 2021
1 parent 2737375 commit cecdcc4
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 84 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package io.quarkus.agroal.deployment;

import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;

import java.sql.Driver;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Priority;
import javax.enterprise.inject.Default;
import javax.inject.Singleton;
import javax.interceptor.Interceptor;
import javax.sql.XADataSource;

import org.jboss.jandex.DotName;
Expand All @@ -24,9 +29,12 @@
import io.quarkus.agroal.runtime.DataSources;
import io.quarkus.agroal.runtime.DataSourcesJdbcBuildTimeConfig;
import io.quarkus.agroal.runtime.TransactionIntegration;
import io.quarkus.agroal.runtime.schema.CleanDatabaseInterceptor;
import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
import io.quarkus.agroal.spi.JdbcDriverBuildItem;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
Expand All @@ -37,6 +45,7 @@
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsTest;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
Expand All @@ -48,6 +57,10 @@
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.narayana.jta.runtime.interceptor.TestTransactionInterceptor;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem;

Expand All @@ -56,6 +69,7 @@ class AgroalProcessor {

private static final Logger log = Logger.getLogger(AgroalProcessor.class);

private static final String CLEAN_DATABASE = "io.quarkus.test.CleanDatabase";
private static final DotName DATA_SOURCE = DotName.createSimple(javax.sql.DataSource.class.getName());

@BuildStep
Expand Down Expand Up @@ -333,4 +347,36 @@ HealthBuildItem addHealthCheck(DataSourcesBuildTimeConfig dataSourcesBuildTimeCo
return new HealthBuildItem("io.quarkus.agroal.runtime.health.DataSourceHealthCheck",
dataSourcesBuildTimeConfig.healthEnabled);
}

@BuildStep
public DevConsoleTemplateInfoBuildItem devConsoleInfo(
List<AggregatedDataSourceBuildTimeConfigBuildItem> dbs) {
return new DevConsoleTemplateInfoBuildItem("dbs",
dbs.stream().map(AggregatedDataSourceBuildTimeConfigBuildItem::getName)
.collect(Collectors.toList()));
}

@BuildStep
@Record(value = STATIC_INIT, optional = true)
DevConsoleRouteBuildItem devConsoleCleanDatabaseHandler(AgroalRecorder recorder) {
return new DevConsoleRouteBuildItem("clean", "POST", recorder.devConsoleCleanDatabaseHandler());
}

@BuildStep(onlyIf = IsTest.class)
void cleanDatabaseSupport(BuildProducer<GeneratedBeanBuildItem> generatedBeanBuildItemBuildProducer,
BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
//generate the annotated interceptor with gizmo
//all the logic is in the parent, but we don't have access to the
//binding annotation here
try (ClassCreator c = ClassCreator.builder()
.classOutput(new GeneratedBeanGizmoAdaptor(generatedBeanBuildItemBuildProducer)).className(
CleanDatabaseInterceptor.class.getName() + "Generated")
.superClass(TestTransactionInterceptor.class).build()) {
c.addAnnotation(CLEAN_DATABASE);
c.addAnnotation(Interceptor.class.getName());
c.addAnnotation(Priority.class).addValue("value", Interceptor.Priority.PLATFORM_BEFORE + 100);
}
additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(CleanDatabaseInterceptor.class)
.addBeanClass(CLEAN_DATABASE).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<th scope="col">Persistence Unit</th>
<th scope="col">Datasource</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{#for pu in info:units}
{#for db in info:dbs}
<tr>
<td>
{pu}
{db}
</td>
<td>
<form method="post" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="name" value="{pu}">
<input type="hidden" name="name" value="{db}">
<input id="invoke" type="submit" value="Invoke" class="btn btn-primary btn-sm">
</form>
</td>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package io.quarkus.agroal.runtime;

import java.util.ServiceLoader;
import java.util.function.Supplier;

import io.agroal.api.AgroalDataSource;
import io.quarkus.agroal.runtime.schema.DatabaseSchemaProvider;
import io.quarkus.datasource.runtime.DataSourcesRuntimeConfig;
import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.ext.web.RoutingContext;

@Recorder
public class AgroalRecorder {
Expand All @@ -29,4 +35,20 @@ public AgroalDataSource get() {
};
}

public Handler<RoutingContext> devConsoleCleanDatabaseHandler() {
// the usual issue of Vert.x hanging on to the first TCCL and setting it on all its threads
final ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
return new DevConsolePostHandler() {
@Override
protected void handlePost(RoutingContext event, MultiMap form) throws Exception {
String name = form.get("name");
ServiceLoader<DatabaseSchemaProvider> dbs = ServiceLoader.load(DatabaseSchemaProvider.class,
Thread.currentThread().getContextClassLoader());
for (DatabaseSchemaProvider i : dbs) {
i.resetDatabase(name);
}
flashMessage(event, "Action invoked");
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.quarkus.agroal.runtime.schema;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class CleanDatabaseInterceptor {

final List<DatabaseSchemaProvider> providers;

public CleanDatabaseInterceptor() {
this.providers = new ArrayList<>();
ServiceLoader<DatabaseSchemaProvider> dbs = ServiceLoader.load(DatabaseSchemaProvider.class,
Thread.currentThread().getContextClassLoader());
for (DatabaseSchemaProvider i : dbs) {
providers.add(i);
}
}

@AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
try {
return context.proceed();
} finally {
for (DatabaseSchemaProvider i : providers) {
i.resetAllDatabases();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.agroal.runtime.schema;

/**
* A service interface that can be used to reset the database for dev and test mode.
*/
public interface DatabaseSchemaProvider {

void resetDatabase(String dbName);

void resetAllDatabases();
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@
import java.util.TreeSet;
import java.util.stream.Collectors;

import javax.annotation.Priority;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Default;
import javax.inject.Singleton;
import javax.interceptor.Interceptor;
import javax.persistence.AttributeConverter;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
Expand Down Expand Up @@ -73,8 +71,6 @@
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.BeanContainerListenerBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem.ExtendedBeanConfigurator;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
Expand All @@ -86,7 +82,6 @@
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsTest;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
Expand All @@ -113,9 +108,6 @@
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.deployment.util.IoUtil;
import io.quarkus.deployment.util.ServiceUtil;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.hibernate.orm.PersistenceUnit;
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem;
Expand All @@ -133,12 +125,10 @@
import io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationStaticDescriptor;
import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies;
import io.quarkus.hibernate.orm.runtime.schema.CleanDatabaseInterceptor;
import io.quarkus.hibernate.orm.runtime.schema.SchemaManagementIntegrator;
import io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver;
import io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver;
import io.quarkus.hibernate.orm.runtime.tenant.TenantResolver;
import io.quarkus.narayana.jta.runtime.interceptor.TestTransactionInterceptor;
import io.quarkus.panache.common.deployment.HibernateEnhancersRegisteredBuildItem;
import io.quarkus.panache.common.deployment.HibernateModelClassCandidatesForFieldAccessBuildItem;
import io.quarkus.runtime.LaunchMode;
Expand All @@ -157,8 +147,6 @@
*/
public final class HibernateOrmProcessor {

private static final String CLEAN_DATABASE = "io.quarkus.test.CleanDatabase";

public static final String HIBERNATE_ORM_CONFIG_PREFIX = "quarkus.hibernate-orm.";
public static final String NO_SQL_LOAD_SCRIPT_FILE = "no-file";

Expand Down Expand Up @@ -650,38 +638,6 @@ public void registerStaticMetamodelClassesForReflection(CombinedIndexBuildItem i
}
}

@BuildStep(onlyIf = IsTest.class)
void cleanDatabaseSupport(BuildProducer<GeneratedBeanBuildItem> generatedBeanBuildItemBuildProducer,
BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
//generate the annotated interceptor with gizmo
//all the logic is in the parent, but we don't have access to the
//binding annotation here
try (ClassCreator c = ClassCreator.builder()
.classOutput(new GeneratedBeanGizmoAdaptor(generatedBeanBuildItemBuildProducer)).className(
CleanDatabaseInterceptor.class.getName() + "Generated")
.superClass(TestTransactionInterceptor.class).build()) {
c.addAnnotation(CLEAN_DATABASE);
c.addAnnotation(Interceptor.class.getName());
c.addAnnotation(Priority.class).addValue("value", Interceptor.Priority.PLATFORM_BEFORE + 100);
}
additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(CleanDatabaseInterceptor.class)
.addBeanClass(CLEAN_DATABASE).build());
}

@BuildStep
public DevConsoleTemplateInfoBuildItem devConsoleInfo(
List<PersistenceUnitDescriptorBuildItem> persistenceUnitDescriptors) {
return new DevConsoleTemplateInfoBuildItem("units",
persistenceUnitDescriptors.stream().map(s -> s.getPersistenceUnitName())
.collect(Collectors.toList()));
}

@BuildStep
@Record(value = STATIC_INIT, optional = true)
DevConsoleRouteBuildItem devConsoleCleanDatabaseHandler(HibernateOrmRecorder recorder) {
return new DevConsoleRouteBuildItem("clean", "POST", recorder.devConsoleCleanDatabaseHandler());
}

private static Optional<String> getSqlLoadScript(Optional<String> sqlLoadScript, LaunchMode launchMode) {
// Explicit file or default Hibernate ORM file.
if (sqlLoadScript.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import io.quarkus.test.QuarkusDevModeTest;
import io.restassured.RestAssured;

public class HibernateDevConsoleTestCase {
public class HibernateSchemaRecreateDevConsoleTestCase {
@RegisterExtension
final static QuarkusDevModeTest TEST = new QuarkusDevModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
Expand All @@ -24,7 +24,7 @@ public void testCleanDatabase() {
RestAssured.when().get("/my-entity/add").then().body(is("MyEntity:added"));
RestAssured.when().get("/my-entity/count").then().body(is("3"));
RestAssured.with()
.redirects().follow(false).formParam("name", "<default>").post("q/dev/io.quarkus.quarkus-hibernate-orm/clean")
.redirects().follow(false).formParam("name", "<default>").post("q/dev/io.quarkus.quarkus-agroal/clean")
.then()
.statusCode(303);
RestAssured.when().get("/my-entity/count").then().body(is("2"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,13 @@
import io.quarkus.arc.Arc;
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.arc.runtime.BeanContainerListener;
import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler;
import io.quarkus.hibernate.orm.runtime.boot.QuarkusPersistenceUnitDefinition;
import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeDescriptor;
import io.quarkus.hibernate.orm.runtime.proxies.PreGeneratedProxies;
import io.quarkus.hibernate.orm.runtime.schema.SchemaManagementIntegrator;
import io.quarkus.hibernate.orm.runtime.session.ForwardingSession;
import io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.ext.web.RoutingContext;

/**
* @author Emmanuel Bernard [email protected]
Expand Down Expand Up @@ -59,6 +55,10 @@ public void setupPersistenceProvider(HibernateOrmRuntimeConfig hibernateOrmRunti
public BeanContainerListener initMetadata(List<QuarkusPersistenceUnitDefinition> parsedPersistenceXmlDescriptors,
Scanner scanner, Collection<Class<? extends Integrator>> additionalIntegrators,
PreGeneratedProxies proxyDefinitions) {
SchemaManagementIntegrator.clearDsMap();
for (QuarkusPersistenceUnitDefinition i : parsedPersistenceXmlDescriptors) {
SchemaManagementIntegrator.mapDatasource(i.getDataSource(), i.getName());
}
return new BeanContainerListener() {
@Override
public void created(BeanContainer beanContainer) {
Expand Down Expand Up @@ -123,17 +123,4 @@ protected Session delegate() {
}
};
}

public Handler<RoutingContext> devConsoleCleanDatabaseHandler() {
// the usual issue of Vert.x hanging on to the first TCCL and setting it on all its threads
final ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
return new DevConsolePostHandler() {
@Override
protected void handlePost(RoutingContext event, MultiMap form) throws Exception {
String name = form.get("name");
SchemaManagementIntegrator.recreateDatabase(name);
flashMessage(event, "Action invoked");
}
};
}
}

This file was deleted.

Loading

0 comments on commit cecdcc4

Please sign in to comment.